[{"data":1,"prerenderedAt":1033},["ShallowReactive",2],{"content-\u002Fdocs\u002Fvalidation\u002Fshowing-errors":3},{"id":4,"title":5,"body":6,"description":1018,"extension":1019,"meta":1020,"metaRows":1021,"navigation":735,"path":1028,"seo":1029,"source":1030,"stem":1031,"__hash__":1032},"docs\u002Fdocs\u002Fvalidation\u002Fshowing-errors.md","Display state and showing errors",{"type":7,"value":8,"toc":1004},"minimark",[9,13,20,23,31,59,65,70,75,86,154,157,281,288,292,295,300,303,319,331,335,338,395,399,402,423,441,448,454,466,473,523,532,551,555,559,651,701,705,712,856,859,865,889,899,960,979,983,1000],[10,11,5],"h1",{"id":12},"display-state-and-showing-errors",[14,15,16],"blockquote",{},[17,18,19],"p",{},"Errors exist in the store the moment validation runs; the display state decides what the UI surfaces, and when.",[21,22],"docs-meta-table",{},[17,24,25,26,30],{},"Every path on a form carries a single display-state verdict, ",[27,28,29],"code",{},"field.displayState",", that is one of four values:",[32,33,34,41,47,53],"ul",{},[35,36,37,40],"li",{},[27,38,39],{},"'idle'",": nothing to surface yet.",[35,42,43,46],{},[27,44,45],{},"'pending'",": a check has been running long enough to earn a spinner.",[35,48,49,52],{},[27,50,51],{},"'error'",": a blocking error is ready to show.",[35,54,55,58],{},[27,56,57],{},"'success'",": the field has passed and earned its green check.",[17,60,61,64],{},[27,62,63],{},"getDisplayState"," is the one heuristic that resolves that verdict, and it runs for every field. Attaform's default holds back until the user has actually interacted with a field, so a fresh-page form does not open with every required field already complaining.",[66,67],"docs-demo",{"label":68,"slug":69},"Display State Demo","display-state",[71,72,74],"h2",{"id":73},"sugar-over-the-verdict","Sugar over the verdict",[17,76,77,78,81,82,85],{},"The four ",[27,79,80],{},"show*"," booleans are exact projections of ",[27,83,84],{},"displayState",", so they can never disagree with it:",[87,88,89,102],"table",{},[90,91,92],"thead",{},[93,94,95,99],"tr",{},[96,97,98],"th",{},"Boolean",[96,100,101],{},"True when",[103,104,105,118,130,142],"tbody",{},[93,106,107,113],{},[108,109,110],"td",{},[27,111,112],{},"showErrors",[108,114,115],{},[27,116,117],{},"displayState === 'error'",[93,119,120,125],{},[108,121,122],{},[27,123,124],{},"showPending",[108,126,127],{},[27,128,129],{},"displayState === 'pending'",[93,131,132,137],{},[108,133,134],{},[27,135,136],{},"showSuccess",[108,138,139],{},[27,140,141],{},"displayState === 'success'",[93,143,144,149],{},[108,145,146],{},[27,147,148],{},"showIdle",[108,150,151],{},[27,152,153],{},"displayState === 'idle'",[17,155,156],{},"Bind whichever reads cleanest. A field that narrates all of its states in one template block:",[158,159,164],"pre",{"className":160,"code":161,"language":162,"meta":163,"style":163},"language-vue shiki shiki-themes github-light github-dark","\u003Cinput v-register=\"form.register('email')\" \u002F>\n\u003Cp v-if=\"form.fields.email.showErrors\">{{ form.fields.email.firstError?.message }}\u003C\u002Fp>\n\u003CSpinner v-else-if=\"form.fields.email.showPending\" \u002F>\n\u003CCheckIcon v-else-if=\"form.fields.email.showSuccess\" \u002F>\n","vue","",[27,165,166,210,238,260],{"__ignoreMap":163},[167,168,171,175,179,183,186,190,193,196,199,202,205,207],"span",{"class":169,"line":170},"line",1,[167,172,174],{"class":173},"sVt8B","\u003C",[167,176,178],{"class":177},"s9eBZ","input",[167,180,182],{"class":181},"sScJk"," v-register",[167,184,185],{"class":173},"=",[167,187,189],{"class":188},"sZZnC","\"",[167,191,192],{"class":173},"form.",[167,194,195],{"class":181},"register",[167,197,198],{"class":173},"(",[167,200,201],{"class":188},"'email'",[167,203,204],{"class":173},")",[167,206,189],{"class":188},[167,208,209],{"class":173}," \u002F>\n",[167,211,213,215,217,221,223,225,228,230,233,235],{"class":169,"line":212},2,[167,214,174],{"class":173},[167,216,17],{"class":177},[167,218,220],{"class":219},"szBVR"," v-if",[167,222,185],{"class":173},[167,224,189],{"class":188},[167,226,227],{"class":173},"form.fields.email.showErrors",[167,229,189],{"class":188},[167,231,232],{"class":173},">{{ form.fields.email.firstError?.message }}\u003C\u002F",[167,234,17],{"class":177},[167,236,237],{"class":173},">\n",[167,239,241,243,246,249,251,253,256,258],{"class":169,"line":240},3,[167,242,174],{"class":173},[167,244,245],{"class":177},"Spinner",[167,247,248],{"class":219}," v-else-if",[167,250,185],{"class":173},[167,252,189],{"class":188},[167,254,255],{"class":173},"form.fields.email.showPending",[167,257,189],{"class":188},[167,259,209],{"class":173},[167,261,263,265,268,270,272,274,277,279],{"class":169,"line":262},4,[167,264,174],{"class":173},[167,266,267],{"class":177},"CheckIcon",[167,269,248],{"class":219},[167,271,185],{"class":173},[167,273,189],{"class":188},[167,275,276],{"class":173},"form.fields.email.showSuccess",[167,278,189],{"class":188},[167,280,209],{"class":173},[17,282,283,284,287],{},"Prefer one branch over the set? Switch on ",[27,285,286],{},"form.fields.email.displayState"," directly. Either way, the same verdict drives the same paint.",[71,289,291],{"id":290},"the-default-heuristic","The default heuristic",[17,293,294],{},"The default opens one timing gate, then resolves the verdict by precedence.",[296,297,299],"h3",{"id":298},"_1-timing-gate","1. Timing gate",[17,301,302],{},"The gate opens when either:",[32,304,305,312],{},[35,306,307,308,311],{},"The form has attempted at least one submit (",[27,309,310],{},"formMeta.submissionAttempts > 0","), OR",[35,313,314,315,318],{},"The field has been edited and then left (",[27,316,317],{},"blurredAfterInteraction",", sticky-true after the first blur that follows a value edit).",[17,320,321,322,324,325,327,328,330],{},"Until the gate opens, ",[27,323,84],{}," is ",[27,326,39],{}," no matter what is in the store. This is \"reward early, punish late.\" A clean tab-through stays quiet: ",[27,329,317],{}," only flips on a blur that follows an edit, so a field the user tabbed through but never edited does not complain until a submit forces the issue. The first pass stays quiet too: editing alone does not open the gate, so the error reveals once the user finishes the pass and leaves the field, never mid-entry, even when the field happened to be tabbed through earlier. And because the bit is sticky and carries no not-focused condition, the gate stays open through a re-focus: once a field has been revealed, fixing its error clears the message live, instead of making the user blur again to see it.",[296,332,334],{"id":333},"_2-precedence","2. Precedence",[17,336,337],{},"Once the gate is open, the default resolves in order:",[339,340,341,357,366,387],"ol",{},[35,342,343,347,348,350,351,356],{},[344,345,346],"strong",{},"Pending (timed)."," While a per-field validation runs, the field is heading for a spinner, but Attaform holds the prior verdict for a short window first. A fast check that settles inside that window never shows ",[27,349,45],{}," at all; a slow one flips to the spinner and is then held a minimum so it cannot blink off. This anti-flash timing is tunable, see ",[352,353,355],"a",{"href":354},"#tune-the-timing","Tune the timing",".",[35,358,359,362,363,365],{},[344,360,361],{},"Error."," An error at the field resolves to ",[27,364,51],{}," (containers roll up their descendants, see below).",[35,367,368,371,372,375,376,379,380,382,383,386],{},[344,369,370],{},"Success."," No error, ",[27,373,374],{},"field.valid",", and the green check is earned: the field is non-blank and ",[27,377,378],{},"dirty",", so the user put valid content there themselves. An empty field that happens to pass, a pre-filled field merely tabbed through, and the post-submit flood of every valid field all stay ",[27,381,39],{}," rather than greening for free. ",[27,384,385],{},"valid"," already waits on the form-wide first validation pass for async schemas, so success never fires before the first real verdict lands.",[35,388,389,392,393,356],{},[344,390,391],{},"Idle."," Anything else, including a valid-but-unearned field (blank or unchanged), stays ",[27,394,39],{},[296,396,398],{"id":397},"rollup-at-containers-and-the-form","Rollup at containers and the form",[17,400,401],{},"A container resolves over the gated verdicts of everything beneath it, so one verdict reflects the whole subtree.",[32,403,404,410],{},[35,405,406,409],{},[344,407,408],{},"Leaves"," resolve on their own error.",[35,411,412,415,416,419,420,422],{},[344,413,414],{},"Containers"," (an object, an array row, the array itself, and the root ",[27,417,418],{},"form.meta",") resolve to ",[27,421,51],{}," when any descendant is showing an error, or when a cross-field error is pinned at the container itself. Each descendant counts only once its own gate has opened, so a sibling that has been blurred never drags an untouched field's error up to the group.",[17,424,425,426,428,429,431,432,434,435,437,438,440],{},"Precedence rolls up too: a container shows ",[27,427,45],{}," while any descendant is still checking, ",[27,430,51],{}," if a gated descendant (or the container's own cross-field check) failed, ",[27,433,57],{}," once every field is valid and at least one is earned, and ",[27,436,39],{}," otherwise. An untouched optional sibling sits ",[27,439,39],{}," without holding the group's success back.",[17,442,443,444,447],{},"That makes ",[27,445,446],{},"form.meta.showErrors"," the natural binding for a form-level \"fix the errors below\" banner: it turns on the moment a field's error becomes visible and off when the last one clears.",[296,449,451,453],{"id":450},"formmeta-during-a-submit",[27,452,418],{}," during a submit",[17,455,456,459,460,462,463,465],{},[27,457,458],{},"form.meta.showPending"," reflects validation, including the validation a submit runs before your handler. A submit re-validates the whole form, so ",[27,461,458],{}," stays lit through that pass and a form-level \"Checking…\" affordance reads true during it. It does not light during the handler itself, because ",[27,464,80],{}," tracks validation, never submission.",[17,467,468,469,472],{},"To gate a submit button across the whole submit (its validation and your handler), bind ",[27,470,471],{},"form.meta.submitting",", the flag that means a submit is in flight:",[158,474,476],{"className":160,"code":475,"language":162,"meta":163,"style":163},"\u003Cbutton type=\"submit\" :disabled=\"form.meta.submitting\">\n  {{ form.meta.submitting ? 'Submitting…' : 'Submit' }}\n\u003C\u002Fbutton>\n",[27,477,478,509,514],{"__ignoreMap":163},[167,479,480,482,485,488,490,493,496,499,501,503,505,507],{"class":169,"line":170},[167,481,174],{"class":173},[167,483,484],{"class":177},"button",[167,486,487],{"class":181}," type",[167,489,185],{"class":173},[167,491,492],{"class":188},"\"submit\"",[167,494,495],{"class":173}," :",[167,497,498],{"class":181},"disabled",[167,500,185],{"class":173},[167,502,189],{"class":188},[167,504,471],{"class":173},[167,506,189],{"class":188},[167,508,237],{"class":173},[167,510,511],{"class":169,"line":212},[167,512,513],{"class":173},"  {{ form.meta.submitting ? 'Submitting…' : 'Submit' }}\n",[167,515,516,519,521],{"class":169,"line":240},[167,517,518],{"class":173},"\u003C\u002F",[167,520,484],{"class":177},[167,522,237],{"class":173},[17,524,525,526,528,529,531],{},"So ",[27,527,458],{}," is the broad \"the form is validating\" light and ",[27,530,471],{}," is the precise \"a submit is running\" flag: use the first for an ambient indicator, the second to gate the button.",[17,533,534,535,537,538,541,542,544,545,548,549,356],{},"This detour is ",[27,536,418],{},"-only: the submit-driven ",[27,539,540],{},"pending"," is applied at the root, so an individual field never flips to a spinner just because a submit is validating, and each field keeps showing its own verdict throughout. And ",[27,543,136],{},", like every verdict, is a validation signal, not a \"submission finished\" one: for a \"Saved\" confirmation, gate on ",[27,546,547],{},"form.meta.submitted"," (which flips once the handler resolves), not on ",[27,550,136],{},[66,552],{"label":553,"slug":554},"Container rollup demo","container-display-state",[71,556,558],{"id":557},"override-per-form","Override per form",[158,560,564],{"className":561,"code":562,"language":563,"meta":163,"style":163},"language-ts shiki shiki-themes github-light github-dark","useForm({\n  schema,\n  getDisplayState: (prev, ctx) =>\n    ctx.field.errors.length > 0 && ctx.field.touched ? { display: 'error' } : { display: 'idle' },\n})\n","ts",[27,565,566,574,579,603,645],{"__ignoreMap":163},[167,567,568,571],{"class":169,"line":170},[167,569,570],{"class":181},"useForm",[167,572,573],{"class":173},"({\n",[167,575,576],{"class":169,"line":212},[167,577,578],{"class":173},"  schema,\n",[167,580,581,584,587,591,594,597,600],{"class":169,"line":240},[167,582,583],{"class":181},"  getDisplayState",[167,585,586],{"class":173},": (",[167,588,590],{"class":589},"s4XuR","prev",[167,592,593],{"class":173},", ",[167,595,596],{"class":589},"ctx",[167,598,599],{"class":173},") ",[167,601,602],{"class":219},"=>\n",[167,604,605,608,612,615,618,621,624,627,630,632,635,638,640,642],{"class":169,"line":262},[167,606,607],{"class":173},"    ctx.field.errors.",[167,609,611],{"class":610},"sj4cs","length",[167,613,614],{"class":219}," >",[167,616,617],{"class":610}," 0",[167,619,620],{"class":219}," &&",[167,622,623],{"class":173}," ctx.field.touched ",[167,625,626],{"class":219},"?",[167,628,629],{"class":173}," { display: ",[167,631,51],{"class":188},[167,633,634],{"class":173}," } ",[167,636,637],{"class":219},":",[167,639,629],{"class":173},[167,641,39],{"class":188},[167,643,644],{"class":173}," },\n",[167,646,648],{"class":169,"line":647},5,[167,649,650],{"class":173},"})\n",[17,652,653,654,657,658,660,661,664,665,667,668,671,672,675,676,678,679,678,681,684,685,688,689,692,693,696,697,700],{},"Pass a custom reducer to bend the rule, for example to reveal errors the moment a field is touched (ignoring focus and submit state). A reducer receives ",[27,655,656],{},"(prev, ctx)",": ",[27,659,590],{}," is the field's previous ",[27,662,663],{},"DisplayMachine",", and ",[27,666,596],{}," carries the field's ",[27,669,670],{},"FieldState"," and the form's ",[27,673,674],{},"FormMeta"," (both with the derived ",[27,677,84],{}," \u002F ",[27,680,80],{},[27,682,683],{},"firstError"," keys omitted, so an accidental self-reference is impossible) plus ",[27,686,687],{},"ctx.validatingSince"," and ",[27,690,691],{},"ctx.now"," for timing. It returns the next machine: ",[27,694,695],{},"{ display }"," at minimum, optionally with a ",[27,698,699],{},"reviewAt"," timestamp telling Attaform when to look again.",[71,702,704],{"id":703},"compose-with-the-default","Compose with the default",[17,706,707,708,711],{},"Adopter reducers can layer on top of ",[27,709,710],{},"defaultDisplayState",". Defer to it for the common case and special-case only the paths you care about:",[158,713,715],{"className":561,"code":714,"language":563,"meta":163,"style":163},"import { defaultDisplayState } from 'attaform'\n\nuseForm({\n  schema,\n  \u002F\u002F Defer everywhere, but never show a success check on `username`.\n  getDisplayState: (prev, ctx) => {\n    const next = defaultDisplayState(prev, ctx)\n    return next.display === 'success' && ctx.field.path[0] === 'username'\n      ? { display: 'idle' }\n      : next\n  },\n})\n",[27,716,717,731,737,743,747,753,774,792,823,836,845,851],{"__ignoreMap":163},[167,718,719,722,725,728],{"class":169,"line":170},[167,720,721],{"class":219},"import",[167,723,724],{"class":173}," { defaultDisplayState } ",[167,726,727],{"class":219},"from",[167,729,730],{"class":188}," 'attaform'\n",[167,732,733],{"class":169,"line":212},[167,734,736],{"emptyLinePlaceholder":735},true,"\n",[167,738,739,741],{"class":169,"line":240},[167,740,570],{"class":181},[167,742,573],{"class":173},[167,744,745],{"class":169,"line":262},[167,746,578],{"class":173},[167,748,749],{"class":169,"line":647},[167,750,752],{"class":751},"sJ8bj","  \u002F\u002F Defer everywhere, but never show a success check on `username`.\n",[167,754,756,758,760,762,764,766,768,771],{"class":169,"line":755},6,[167,757,583],{"class":181},[167,759,586],{"class":173},[167,761,590],{"class":589},[167,763,593],{"class":173},[167,765,596],{"class":589},[167,767,599],{"class":173},[167,769,770],{"class":219},"=>",[167,772,773],{"class":173}," {\n",[167,775,777,780,783,786,789],{"class":169,"line":776},7,[167,778,779],{"class":219},"    const",[167,781,782],{"class":610}," next",[167,784,785],{"class":219}," =",[167,787,788],{"class":181}," defaultDisplayState",[167,790,791],{"class":173},"(prev, ctx)\n",[167,793,795,798,801,804,807,809,812,815,818,820],{"class":169,"line":794},8,[167,796,797],{"class":219},"    return",[167,799,800],{"class":173}," next.display ",[167,802,803],{"class":219},"===",[167,805,806],{"class":188}," 'success'",[167,808,620],{"class":219},[167,810,811],{"class":173}," ctx.field.path[",[167,813,814],{"class":610},"0",[167,816,817],{"class":173},"] ",[167,819,803],{"class":219},[167,821,822],{"class":188}," 'username'\n",[167,824,826,829,831,833],{"class":169,"line":825},9,[167,827,828],{"class":219},"      ?",[167,830,629],{"class":173},[167,832,39],{"class":188},[167,834,835],{"class":173}," }\n",[167,837,839,842],{"class":169,"line":838},10,[167,840,841],{"class":219},"      :",[167,843,844],{"class":173}," next\n",[167,846,848],{"class":169,"line":847},11,[167,849,850],{"class":173},"  },\n",[167,852,854],{"class":169,"line":853},12,[167,855,650],{"class":173},[71,857,355],{"id":858},"tune-the-timing",[17,860,861,862,864],{},"The spinner is anti-flash by default. A validation that settles quickly never shows ",[27,863,45],{}," at all, and once a spinner appears it stays up for a minimum so it cannot blink off the instant the check lands. Two timings shape this, both in milliseconds:",[32,866,867,877],{},[35,868,869,872,873,876],{},[27,870,871],{},"showDelay"," (default ",[27,874,875],{},"100","): how long a validation may run before its spinner is allowed to show. Anything that settles inside this window stays on its prior verdict, so a synchronous or microtask-fast check never flashes a spinner on every keystroke.",[35,878,879,872,882,885,886,888],{},[27,880,881],{},"minVisible",[27,883,884],{},"120","): once shown, the minimum the spinner stays up, so a check that lands just past ",[27,887,871],{}," does not flash it on and immediately off.",[17,890,891,892,895,896,637],{},"The shipped values live in ",[27,893,894],{},"DEFAULT_TIMINGS",". To retune, build a default with ",[27,897,898],{},"makeDefaultDisplayState",[158,900,902],{"className":561,"code":901,"language":563,"meta":163,"style":163},"import { makeDefaultDisplayState } from 'attaform'\n\nuseForm({\n  schema,\n  \u002F\u002F Tighter: a spinner after 50ms, held for 200ms once shown.\n  getDisplayState: makeDefaultDisplayState({ showDelay: 50, minVisible: 200 }),\n})\n",[27,903,904,915,919,925,929,934,956],{"__ignoreMap":163},[167,905,906,908,911,913],{"class":169,"line":170},[167,907,721],{"class":219},[167,909,910],{"class":173}," { makeDefaultDisplayState } ",[167,912,727],{"class":219},[167,914,730],{"class":188},[167,916,917],{"class":169,"line":212},[167,918,736],{"emptyLinePlaceholder":735},[167,920,921,923],{"class":169,"line":240},[167,922,570],{"class":181},[167,924,573],{"class":173},[167,926,927],{"class":169,"line":262},[167,928,578],{"class":173},[167,930,931],{"class":169,"line":647},[167,932,933],{"class":751},"  \u002F\u002F Tighter: a spinner after 50ms, held for 200ms once shown.\n",[167,935,936,939,941,944,947,950,953],{"class":169,"line":755},[167,937,938],{"class":173},"  getDisplayState: ",[167,940,898],{"class":181},[167,942,943],{"class":173},"({ showDelay: ",[167,945,946],{"class":610},"50",[167,948,949],{"class":173},", minVisible: ",[167,951,952],{"class":610},"200",[167,954,955],{"class":173}," }),\n",[167,957,958],{"class":169,"line":776},[167,959,650],{"class":173},[17,961,962,963,593,966,593,968,971,972,974,975,978],{},"This shapes only the display projection. ",[27,964,965],{},"errors",[27,967,385],{},[27,969,970],{},"validating",", and the underlying validation all run exactly as before; only when the spinner appears and how long it lingers change. For total control, a from-scratch reducer owns its own timing by returning a ",[27,973,699],{}," (an absolute ",[27,976,977],{},"Date.now()"," stamp) to tell Attaform when to re-evaluate the field.",[71,980,982],{"id":981},"where-to-next","Where to next",[32,984,985,993],{},[35,986,987,992],{},[352,988,990],{"href":989},"\u002Fdocs\u002Freading-the-form\u002Ferrors",[27,991,965],{},": the error reads themselves.",[35,994,995,999],{},[352,996,998],{"href":997},"\u002Fdocs\u002Fvalidation\u002Fwhen-validation-runs","When validation runs",": the timing knob for the underlying validation pass.",[1001,1002,1003],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":163,"searchDepth":212,"depth":212,"links":1005},[1006,1007,1014,1015,1016,1017],{"id":73,"depth":212,"text":74},{"id":290,"depth":212,"text":291,"children":1008},[1009,1010,1011,1012],{"id":298,"depth":240,"text":299},{"id":333,"depth":240,"text":334},{"id":397,"depth":240,"text":398},{"id":450,"depth":240,"text":1013},"form.meta during a submit",{"id":557,"depth":212,"text":558},{"id":703,"depth":212,"text":704},{"id":858,"depth":212,"text":355},{"id":981,"depth":212,"text":982},"getDisplayState resolves every path to one verdict (idle, pending, error, or success). The default holds errors back until a field is edited and blurred or the form is submitted, surfaces a spinner while async checks run, and confirms a clean field with an earned success, never duplicating errors a more-specific descendant already renders.","md",{},[1022,1025,1026],{"label":1023,"value":1024},"Category","Option",{"label":1024,"value":63,"kind":27},{"label":1027,"value":710,"kind":27},"Default","\u002Fdocs\u002Fvalidation\u002Fshowing-errors",{"title":5,"description":1018},null,"docs\u002Fvalidation\u002Fshowing-errors","meoLgHXlCO_Oh7NS6szCmk5Sip8w4DprDDOPg3Pqc64",1780949760448]