[{"data":1,"prerenderedAt":642},["ShallowReactive",2],{"content-\u002Fdocs\u002Fgetting-started\u002Ffrom-schema-to-inputs":3},{"id":4,"title":5,"body":6,"description":625,"extension":626,"meta":627,"metaRows":628,"navigation":128,"path":629,"seo":630,"source":628,"stem":640,"__hash__":641},"docs\u002Fdocs\u002Fgetting-started\u002Ffrom-schema-to-inputs.md","From schema to inputs",{"type":7,"value":8,"toc":619},"minimark",[9,13,20,23,65,70,75,81,264,289,296,300,309,353,356,408,412,415,578,588,592,615],[10,11,5],"h1",{"id":12},"from-schema-to-inputs",[14,15,16],"blockquote",{},[17,18,19],"p",{},"One directive binds any schema path to any native input. Attaform handles the read, the write, and the coercion.",[21,22],"docs-meta-table",{},[17,24,25,26,30,31,34,35,38,39,42,43,46,47,50,51,54,55,58,59,64],{},"The demo binds five native inputs (a text input for ",[27,28,29],"code",{},"fullName",", a numeric ",[27,32,33],{},"age",", a country ",[27,36,37],{},"\u003Cselect>",", a ",[27,40,41],{},"newsletter"," checkbox, a ",[27,44,45],{},"bio"," textarea) against a single Zod schema. Type into any of them and the live ",[27,48,49],{},"form.values"," JSON below the form updates with the right type: the number input lands as a ",[27,52,53],{},"number",", the checkbox as a ",[27,56,57],{},"boolean",", the select as its enum literal. One directive handles every shape; the ",[60,61,63],"a",{"href":62},"#the-register-v-register-pair","register \u002F v-register pair"," section unpacks why.",[66,67],"docs-demo",{"label":68,"slug":69},"Inputs Demo","schema-to-inputs",[71,72,74],"h2",{"id":73},"setting-up-the-form","Setting up the form",[17,76,77,80],{},[27,78,79],{},"useForm"," is the entry point. Hand it a Zod schema; it returns a reactive form carrying every binding helper this page uses. Save the return value and reach for the pieces by name:",[82,83,88],"pre",{"className":84,"code":85,"language":86,"meta":87,"style":87},"language-ts shiki shiki-themes github-light github-dark","import { useForm } from 'attaform\u002Fzod'\nimport { z } from 'zod'\n\nconst schema = z.object({\n  fullName: z.string().min(2),\n  age: z.number().int().min(13),\n  country: z.string(),\n  newsletter: z.boolean(),\n  bio: z.string().optional(),\n})\n\nconst form = useForm({ schema })\n","ts","",[27,89,90,110,123,130,153,177,201,212,222,237,243,248],{"__ignoreMap":87},[91,92,95,99,103,106],"span",{"class":93,"line":94},"line",1,[91,96,98],{"class":97},"szBVR","import",[91,100,102],{"class":101},"sVt8B"," { useForm } ",[91,104,105],{"class":97},"from",[91,107,109],{"class":108},"sZZnC"," 'attaform\u002Fzod'\n",[91,111,113,115,118,120],{"class":93,"line":112},2,[91,114,98],{"class":97},[91,116,117],{"class":101}," { z } ",[91,119,105],{"class":97},[91,121,122],{"class":108}," 'zod'\n",[91,124,126],{"class":93,"line":125},3,[91,127,129],{"emptyLinePlaceholder":128},true,"\n",[91,131,133,136,140,143,146,150],{"class":93,"line":132},4,[91,134,135],{"class":97},"const",[91,137,139],{"class":138},"sj4cs"," schema",[91,141,142],{"class":97}," =",[91,144,145],{"class":101}," z.",[91,147,149],{"class":148},"sScJk","object",[91,151,152],{"class":101},"({\n",[91,154,156,159,162,165,168,171,174],{"class":93,"line":155},5,[91,157,158],{"class":101},"  fullName: z.",[91,160,161],{"class":148},"string",[91,163,164],{"class":101},"().",[91,166,167],{"class":148},"min",[91,169,170],{"class":101},"(",[91,172,173],{"class":138},"2",[91,175,176],{"class":101},"),\n",[91,178,180,183,185,187,190,192,194,196,199],{"class":93,"line":179},6,[91,181,182],{"class":101},"  age: z.",[91,184,53],{"class":148},[91,186,164],{"class":101},[91,188,189],{"class":148},"int",[91,191,164],{"class":101},[91,193,167],{"class":148},[91,195,170],{"class":101},[91,197,198],{"class":138},"13",[91,200,176],{"class":101},[91,202,204,207,209],{"class":93,"line":203},7,[91,205,206],{"class":101},"  country: z.",[91,208,161],{"class":148},[91,210,211],{"class":101},"(),\n",[91,213,215,218,220],{"class":93,"line":214},8,[91,216,217],{"class":101},"  newsletter: z.",[91,219,57],{"class":148},[91,221,211],{"class":101},[91,223,225,228,230,232,235],{"class":93,"line":224},9,[91,226,227],{"class":101},"  bio: z.",[91,229,161],{"class":148},[91,231,164],{"class":101},[91,233,234],{"class":148},"optional",[91,236,211],{"class":101},[91,238,240],{"class":93,"line":239},10,[91,241,242],{"class":101},"})\n",[91,244,246],{"class":93,"line":245},11,[91,247,129],{"emptyLinePlaceholder":128},[91,249,251,253,256,258,261],{"class":93,"line":250},12,[91,252,135],{"class":97},[91,254,255],{"class":138}," form",[91,257,142],{"class":97},[91,259,260],{"class":148}," useForm",[91,262,263],{"class":101},"({ schema })\n",[265,266,267,278,284],"ul",{},[268,269,270,273,274,277],"li",{},[27,271,272],{},"form.register(path)"," is the per-input binding factory. The template hands its return value to ",[27,275,276],{},"v-register",".",[268,279,280,283],{},[27,281,282],{},"form.fields"," is the reactive map of per-field state (errors, focus, touched, etc).",[268,285,286,288],{},[27,287,49],{}," is the reactive parsed values, paths and types straight from the schema.",[17,290,291,292,295],{},"The rest of this page reaches for these three off the same ",[27,293,294],{},"form"," handle. The demo above does the same.",[71,297,299],{"id":298},"the-register-v-register-pair","The register \u002F v-register pair",[17,301,302,305,306,308],{},[27,303,304],{},"form.register('email')"," returns a small binding object the ",[27,307,276],{}," directive consumes. Hand it off in the template:",[82,310,314],{"className":311,"code":312,"language":313,"meta":87,"style":87},"language-vue shiki shiki-themes github-light github-dark","\u003Cinput v-register=\"form.register('email')\" \u002F>\n","vue",[27,315,316],{"__ignoreMap":87},[91,317,318,321,325,328,331,334,337,340,342,345,348,350],{"class":93,"line":94},[91,319,320],{"class":101},"\u003C",[91,322,324],{"class":323},"s9eBZ","input",[91,326,327],{"class":148}," v-register",[91,329,330],{"class":101},"=",[91,332,333],{"class":108},"\"",[91,335,336],{"class":101},"form.",[91,338,339],{"class":148},"register",[91,341,170],{"class":101},[91,343,344],{"class":108},"'email'",[91,346,347],{"class":101},")",[91,349,333],{"class":108},[91,351,352],{"class":101}," \u002F>\n",[17,354,355],{},"That's the whole binding. The directive:",[265,357,358,365,382,393],{},[268,359,360,361,364],{},"Reads from ",[27,362,363],{},"form.values.email"," and writes the current value into the DOM input.",[268,366,367,368,370,371,373,374,377,378,381],{},"Writes back to ",[27,369,363],{}," on every ",[27,372,324],{}," event (or ",[27,375,376],{},"change"," \u002F ",[27,379,380],{},"blur"," with directive modifiers).",[268,383,384,385,388,389,392],{},"Coerces values per the schema, so ",[27,386,387],{},"type=\"number\""," lands in ",[27,390,391],{},"form.values.age"," as a number, not a string.",[268,394,395,396,399,400,407],{},"Tracks per-field interaction state on ",[27,397,398],{},"form.fields.email"," (focused, touched, blurred, blank, plus errors and a few more). The ",[60,401,403,406],{"href":402},"\u002Fdocs\u002Freading-the-form\u002Ffields",[27,404,405],{},"fields"," page"," names every bit.",[71,409,411],{"id":410},"native-inputs-native-types","Native inputs, native types",[17,413,414],{},"The directive works on every input shape Vue exposes. The schema dictates the value type; the input attribute dictates the DOM control:",[82,416,418],{"className":311,"code":417,"language":313,"meta":87,"style":87},"\u003Cinput v-register=\"form.register('fullName')\" \u002F>\n\u003Cinput v-register=\"form.register('age')\" type=\"number\" \u002F>\n\u003Cinput v-register=\"form.register('newsletter')\" type=\"checkbox\" \u002F>\n\u003Cselect v-register=\"form.register('country')\">…\u003C\u002Fselect>\n\u003Ctextarea v-register=\"form.register('bio')\" \u002F>\n",[27,419,420,447,482,516,550],{"__ignoreMap":87},[91,421,422,424,426,428,430,432,434,436,438,441,443,445],{"class":93,"line":94},[91,423,320],{"class":101},[91,425,324],{"class":323},[91,427,327],{"class":148},[91,429,330],{"class":101},[91,431,333],{"class":108},[91,433,336],{"class":101},[91,435,339],{"class":148},[91,437,170],{"class":101},[91,439,440],{"class":108},"'fullName'",[91,442,347],{"class":101},[91,444,333],{"class":108},[91,446,352],{"class":101},[91,448,449,451,453,455,457,459,461,463,465,468,470,472,475,477,480],{"class":93,"line":112},[91,450,320],{"class":101},[91,452,324],{"class":323},[91,454,327],{"class":148},[91,456,330],{"class":101},[91,458,333],{"class":108},[91,460,336],{"class":101},[91,462,339],{"class":148},[91,464,170],{"class":101},[91,466,467],{"class":108},"'age'",[91,469,347],{"class":101},[91,471,333],{"class":108},[91,473,474],{"class":148}," type",[91,476,330],{"class":101},[91,478,479],{"class":108},"\"number\"",[91,481,352],{"class":101},[91,483,484,486,488,490,492,494,496,498,500,503,505,507,509,511,514],{"class":93,"line":125},[91,485,320],{"class":101},[91,487,324],{"class":323},[91,489,327],{"class":148},[91,491,330],{"class":101},[91,493,333],{"class":108},[91,495,336],{"class":101},[91,497,339],{"class":148},[91,499,170],{"class":101},[91,501,502],{"class":108},"'newsletter'",[91,504,347],{"class":101},[91,506,333],{"class":108},[91,508,474],{"class":148},[91,510,330],{"class":101},[91,512,513],{"class":108},"\"checkbox\"",[91,515,352],{"class":101},[91,517,518,520,523,525,527,529,531,533,535,538,540,542,545,547],{"class":93,"line":132},[91,519,320],{"class":101},[91,521,522],{"class":323},"select",[91,524,327],{"class":148},[91,526,330],{"class":101},[91,528,333],{"class":108},[91,530,336],{"class":101},[91,532,339],{"class":148},[91,534,170],{"class":101},[91,536,537],{"class":108},"'country'",[91,539,347],{"class":101},[91,541,333],{"class":108},[91,543,544],{"class":101},">…\u003C\u002F",[91,546,522],{"class":323},[91,548,549],{"class":101},">\n",[91,551,552,554,557,559,561,563,565,567,569,572,574,576],{"class":93,"line":155},[91,553,320],{"class":101},[91,555,556],{"class":323},"textarea",[91,558,327],{"class":148},[91,560,330],{"class":101},[91,562,333],{"class":108},[91,564,336],{"class":101},[91,566,339],{"class":148},[91,568,170],{"class":101},[91,570,571],{"class":108},"'bio'",[91,573,347],{"class":101},[91,575,333],{"class":108},[91,577,352],{"class":101},[17,579,580,581,584,585,587],{},"No wrapper component, no per-type binding logic. Your ",[27,582,583],{},"\u003Cinput>"," stays a native ",[27,586,583],{},"; Attaform sits at the directive layer.",[71,589,591],{"id":590},"where-to-next","Where to next",[265,593,594,605],{},[268,595,596,600,601,604],{},[60,597,599],{"href":598},"\u002Fdocs\u002Fgetting-started\u002Ffrom-inputs-to-submit","From inputs to submit",": close the loop with ",[27,602,603],{},"handleSubmit",", the helper that gates dispatch on validation.",[268,606,607,614],{},[60,608,610,611,613],{"href":609},"\u002Fdocs\u002Fbinding-inputs\u002Fv-register","The ",[27,612,276],{}," directive",": the full directive surface (modifiers, transforms, custom assigners).",[616,617,618],"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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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}",{"title":87,"searchDepth":112,"depth":112,"links":620},[621,622,623,624],{"id":73,"depth":112,"text":74},{"id":298,"depth":112,"text":299},{"id":410,"depth":112,"text":411},{"id":590,"depth":112,"text":591},"[object Object]","md",{},null,"\u002Fdocs\u002Fgetting-started\u002Ffrom-schema-to-inputs",{"title":5,"description":631},{"The register + v-register pair turns a schema path into a typed two-way binding for any native input element":632,"metaRows":633},"text, number, select, checkbox, textarea.",[634,637],{"label":635,"value":636},"Read time","~4 minutes",{"label":638,"value":639},"Builds on","Your first schema","docs\u002Fgetting-started\u002Ffrom-schema-to-inputs","Cbbj8kXrfByNT5S7geXl2NV6iUGiHkIhvV4BuQmOeQk",1780949757428]