mobx-formkit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/AGENTS.md +761 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/lib/ArrayMap.d.ts +50 -0
  5. package/lib/ArrayMap.d.ts.map +1 -0
  6. package/lib/ArrayMap.js +151 -0
  7. package/lib/ArrayMap.js.map +1 -0
  8. package/lib/Base.d.ts +237 -0
  9. package/lib/Base.d.ts.map +1 -0
  10. package/lib/Base.js +872 -0
  11. package/lib/Base.js.map +1 -0
  12. package/lib/Bindings.d.ts +28 -0
  13. package/lib/Bindings.d.ts.map +1 -0
  14. package/lib/Bindings.js +73 -0
  15. package/lib/Bindings.js.map +1 -0
  16. package/lib/Field.d.ts +195 -0
  17. package/lib/Field.d.ts.map +1 -0
  18. package/lib/Field.js +734 -0
  19. package/lib/Field.js.map +1 -0
  20. package/lib/Form.d.ts +75 -0
  21. package/lib/Form.d.ts.map +1 -0
  22. package/lib/Form.js +199 -0
  23. package/lib/Form.js.map +1 -0
  24. package/lib/Options.d.ts +9 -0
  25. package/lib/Options.d.ts.map +1 -0
  26. package/lib/Options.js +85 -0
  27. package/lib/Options.js.map +1 -0
  28. package/lib/State.d.ts +43 -0
  29. package/lib/State.d.ts.map +1 -0
  30. package/lib/State.js +147 -0
  31. package/lib/State.js.map +1 -0
  32. package/lib/Validator.d.ts +25 -0
  33. package/lib/Validator.d.ts.map +1 -0
  34. package/lib/Validator.js +143 -0
  35. package/lib/Validator.js.map +1 -0
  36. package/lib/compat.d.ts +29 -0
  37. package/lib/compat.d.ts.map +1 -0
  38. package/lib/compat.js +117 -0
  39. package/lib/compat.js.map +1 -0
  40. package/lib/composer.d.ts +26 -0
  41. package/lib/composer.d.ts.map +1 -0
  42. package/lib/composer.js +65 -0
  43. package/lib/composer.js.map +1 -0
  44. package/lib/esm/ArrayMap.js +146 -0
  45. package/lib/esm/ArrayMap.js.map +1 -0
  46. package/lib/esm/Base.js +868 -0
  47. package/lib/esm/Base.js.map +1 -0
  48. package/lib/esm/Bindings.js +69 -0
  49. package/lib/esm/Bindings.js.map +1 -0
  50. package/lib/esm/Field.js +730 -0
  51. package/lib/esm/Field.js.map +1 -0
  52. package/lib/esm/Form.js +195 -0
  53. package/lib/esm/Form.js.map +1 -0
  54. package/lib/esm/Options.js +81 -0
  55. package/lib/esm/Options.js.map +1 -0
  56. package/lib/esm/State.js +143 -0
  57. package/lib/esm/State.js.map +1 -0
  58. package/lib/esm/Validator.js +139 -0
  59. package/lib/esm/Validator.js.map +1 -0
  60. package/lib/esm/compat.js +96 -0
  61. package/lib/esm/compat.js.map +1 -0
  62. package/lib/esm/composer.js +63 -0
  63. package/lib/esm/composer.js.map +1 -0
  64. package/lib/esm/index.js +8 -0
  65. package/lib/esm/index.js.map +1 -0
  66. package/lib/esm/models/FieldProps.js +116 -0
  67. package/lib/esm/models/FieldProps.js.map +1 -0
  68. package/lib/esm/models/OptionsModel.js +51 -0
  69. package/lib/esm/models/OptionsModel.js.map +1 -0
  70. package/lib/esm/models/StateInterface.js +9 -0
  71. package/lib/esm/models/StateInterface.js.map +1 -0
  72. package/lib/esm/models/ValidatorInterface.js +8 -0
  73. package/lib/esm/models/ValidatorInterface.js.map +1 -0
  74. package/lib/esm/parser.js +235 -0
  75. package/lib/esm/parser.js.map +1 -0
  76. package/lib/esm/props.js +118 -0
  77. package/lib/esm/props.js.map +1 -0
  78. package/lib/esm/utils.js +156 -0
  79. package/lib/esm/utils.js.map +1 -0
  80. package/lib/esm/validators/AJV.js +105 -0
  81. package/lib/esm/validators/AJV.js.map +1 -0
  82. package/lib/esm/validators/DVR.js +104 -0
  83. package/lib/esm/validators/DVR.js.map +1 -0
  84. package/lib/esm/validators/JOI.js +54 -0
  85. package/lib/esm/validators/JOI.js.map +1 -0
  86. package/lib/esm/validators/VALIBOT.js +50 -0
  87. package/lib/esm/validators/VALIBOT.js.map +1 -0
  88. package/lib/esm/validators/VINEJS.js +60 -0
  89. package/lib/esm/validators/VINEJS.js.map +1 -0
  90. package/lib/esm/validators/VJF.js +88 -0
  91. package/lib/esm/validators/VJF.js.map +1 -0
  92. package/lib/esm/validators/YUP.js +69 -0
  93. package/lib/esm/validators/YUP.js.map +1 -0
  94. package/lib/esm/validators/ZOD.js +43 -0
  95. package/lib/esm/validators/ZOD.js.map +1 -0
  96. package/lib/index.d.ts +7 -0
  97. package/lib/index.d.ts.map +1 -0
  98. package/lib/index.js +15 -0
  99. package/lib/index.js.map +1 -0
  100. package/lib/models/BaseInterface.d.ts +40 -0
  101. package/lib/models/BaseInterface.d.ts.map +1 -0
  102. package/lib/models/BindingsInterface.d.ts +18 -0
  103. package/lib/models/BindingsInterface.d.ts.map +1 -0
  104. package/lib/models/FieldInterface.d.ts +88 -0
  105. package/lib/models/FieldInterface.d.ts.map +1 -0
  106. package/lib/models/FieldProps.d.ts +111 -0
  107. package/lib/models/FieldProps.d.ts.map +1 -0
  108. package/lib/models/FieldProps.js +116 -0
  109. package/lib/models/FieldProps.js.map +1 -0
  110. package/lib/models/FormInterface.d.ts +184 -0
  111. package/lib/models/FormInterface.d.ts.map +1 -0
  112. package/lib/models/OptionsInterface.d.ts +8 -0
  113. package/lib/models/OptionsInterface.d.ts.map +1 -0
  114. package/lib/models/OptionsModel.d.ts +99 -0
  115. package/lib/models/OptionsModel.d.ts.map +1 -0
  116. package/lib/models/OptionsModel.js +51 -0
  117. package/lib/models/OptionsModel.js.map +1 -0
  118. package/lib/models/SharedActionsInterface.d.ts +27 -0
  119. package/lib/models/SharedActionsInterface.d.ts.map +1 -0
  120. package/lib/models/SharedEventsInterface.d.ts +8 -0
  121. package/lib/models/SharedEventsInterface.d.ts.map +1 -0
  122. package/lib/models/SharedHelpersInterface.d.ts +26 -0
  123. package/lib/models/SharedHelpersInterface.d.ts.map +1 -0
  124. package/lib/models/SharedInitializerInterface.d.ts +6 -0
  125. package/lib/models/SharedInitializerInterface.d.ts.map +1 -0
  126. package/lib/models/SharedUtilsInterface.d.ts +10 -0
  127. package/lib/models/SharedUtilsInterface.d.ts.map +1 -0
  128. package/lib/models/StateInterface.d.ts +37 -0
  129. package/lib/models/StateInterface.d.ts.map +1 -0
  130. package/lib/models/StateInterface.js +9 -0
  131. package/lib/models/StateInterface.js.map +1 -0
  132. package/lib/models/ValidatorInterface.d.ts +74 -0
  133. package/lib/models/ValidatorInterface.d.ts.map +1 -0
  134. package/lib/models/ValidatorInterface.js +8 -0
  135. package/lib/models/ValidatorInterface.js.map +1 -0
  136. package/lib/parser.d.ts +13 -0
  137. package/lib/parser.d.ts.map +1 -0
  138. package/lib/parser.js +247 -0
  139. package/lib/parser.js.map +1 -0
  140. package/lib/props.d.ts +14 -0
  141. package/lib/props.d.ts.map +1 -0
  142. package/lib/props.js +120 -0
  143. package/lib/props.js.map +1 -0
  144. package/lib/utils.d.ts +34 -0
  145. package/lib/utils.d.ts.map +1 -0
  146. package/lib/utils.js +182 -0
  147. package/lib/utils.js.map +1 -0
  148. package/lib/validators/AJV.d.ts +4 -0
  149. package/lib/validators/AJV.d.ts.map +1 -0
  150. package/lib/validators/AJV.js +109 -0
  151. package/lib/validators/AJV.js.map +1 -0
  152. package/lib/validators/DVR.d.ts +4 -0
  153. package/lib/validators/DVR.d.ts.map +1 -0
  154. package/lib/validators/DVR.js +108 -0
  155. package/lib/validators/DVR.js.map +1 -0
  156. package/lib/validators/JOI.d.ts +4 -0
  157. package/lib/validators/JOI.d.ts.map +1 -0
  158. package/lib/validators/JOI.js +58 -0
  159. package/lib/validators/JOI.js.map +1 -0
  160. package/lib/validators/VALIBOT.d.ts +4 -0
  161. package/lib/validators/VALIBOT.d.ts.map +1 -0
  162. package/lib/validators/VALIBOT.js +73 -0
  163. package/lib/validators/VALIBOT.js.map +1 -0
  164. package/lib/validators/VINEJS.d.ts +4 -0
  165. package/lib/validators/VINEJS.d.ts.map +1 -0
  166. package/lib/validators/VINEJS.js +64 -0
  167. package/lib/validators/VINEJS.js.map +1 -0
  168. package/lib/validators/VJF.d.ts +4 -0
  169. package/lib/validators/VJF.d.ts.map +1 -0
  170. package/lib/validators/VJF.js +92 -0
  171. package/lib/validators/VJF.js.map +1 -0
  172. package/lib/validators/YUP.d.ts +4 -0
  173. package/lib/validators/YUP.d.ts.map +1 -0
  174. package/lib/validators/YUP.js +73 -0
  175. package/lib/validators/YUP.js.map +1 -0
  176. package/lib/validators/ZOD.d.ts +4 -0
  177. package/lib/validators/ZOD.d.ts.map +1 -0
  178. package/lib/validators/ZOD.js +47 -0
  179. package/lib/validators/ZOD.js.map +1 -0
  180. package/package.json +183 -0
  181. package/src/ArrayMap.ts +165 -0
  182. package/src/Base.ts +1229 -0
  183. package/src/Bindings.ts +85 -0
  184. package/src/Field.ts +1034 -0
  185. package/src/Form.ts +264 -0
  186. package/src/Options.ts +91 -0
  187. package/src/State.ts +183 -0
  188. package/src/Validator.ts +177 -0
  189. package/src/compat.ts +104 -0
  190. package/src/composer.ts +82 -0
  191. package/src/index.ts +7 -0
  192. package/src/models/BaseInterface.ts +45 -0
  193. package/src/models/BindingsInterface.ts +25 -0
  194. package/src/models/FieldInterface.ts +97 -0
  195. package/src/models/FieldProps.ts +116 -0
  196. package/src/models/FormInterface.ts +203 -0
  197. package/src/models/OptionsInterface.ts +9 -0
  198. package/src/models/OptionsModel.ts +100 -0
  199. package/src/models/SharedActionsInterface.ts +30 -0
  200. package/src/models/SharedEventsInterface.ts +8 -0
  201. package/src/models/SharedHelpersInterface.ts +12 -0
  202. package/src/models/SharedInitializerInterface.ts +6 -0
  203. package/src/models/SharedUtilsInterface.ts +10 -0
  204. package/src/models/StateInterface.ts +44 -0
  205. package/src/models/ValidatorInterface.ts +88 -0
  206. package/src/parser.ts +327 -0
  207. package/src/props.ts +127 -0
  208. package/src/utils.ts +218 -0
  209. package/src/validators/AJV.ts +140 -0
  210. package/src/validators/DVR.ts +145 -0
  211. package/src/validators/JOI.ts +79 -0
  212. package/src/validators/VALIBOT.ts +70 -0
  213. package/src/validators/VINEJS.ts +83 -0
  214. package/src/validators/VJF.ts +123 -0
  215. package/src/validators/YUP.ts +90 -0
  216. package/src/validators/ZOD.ts +66 -0
  217. package/umd/MobxFormkit.umd.js +3093 -0
  218. package/umd/MobxFormkit.umd.js.map +1 -0
  219. package/umd/MobxFormkit.umd.min.js +2 -0
  220. package/umd/MobxFormkit.umd.min.js.map +1 -0
  221. package/umd/MobxFormkitComposer.umd.js +183 -0
  222. package/umd/MobxFormkitComposer.umd.js.map +1 -0
  223. package/umd/MobxFormkitComposer.umd.min.js +2 -0
  224. package/umd/MobxFormkitComposer.umd.min.js.map +1 -0
  225. package/umd/MobxFormkitValidatorAJV.umd.js +113 -0
  226. package/umd/MobxFormkitValidatorAJV.umd.js.map +1 -0
  227. package/umd/MobxFormkitValidatorAJV.umd.min.js +2 -0
  228. package/umd/MobxFormkitValidatorAJV.umd.min.js.map +1 -0
  229. package/umd/MobxFormkitValidatorDVR.umd.js +110 -0
  230. package/umd/MobxFormkitValidatorDVR.umd.js.map +1 -0
  231. package/umd/MobxFormkitValidatorDVR.umd.min.js +2 -0
  232. package/umd/MobxFormkitValidatorDVR.umd.min.js.map +1 -0
  233. package/umd/MobxFormkitValidatorJOI.umd.js +62 -0
  234. package/umd/MobxFormkitValidatorJOI.umd.js.map +1 -0
  235. package/umd/MobxFormkitValidatorJOI.umd.min.js +2 -0
  236. package/umd/MobxFormkitValidatorJOI.umd.min.js.map +1 -0
  237. package/umd/MobxFormkitValidatorVALIBOT.umd.js +8032 -0
  238. package/umd/MobxFormkitValidatorVALIBOT.umd.js.map +1 -0
  239. package/umd/MobxFormkitValidatorVALIBOT.umd.min.js +2 -0
  240. package/umd/MobxFormkitValidatorVALIBOT.umd.min.js.map +1 -0
  241. package/umd/MobxFormkitValidatorVINEJS.umd.js +11737 -0
  242. package/umd/MobxFormkitValidatorVINEJS.umd.js.map +1 -0
  243. package/umd/MobxFormkitValidatorVINEJS.umd.min.js +2 -0
  244. package/umd/MobxFormkitValidatorVINEJS.umd.min.js.map +1 -0
  245. package/umd/MobxFormkitValidatorVJF.umd.js +96 -0
  246. package/umd/MobxFormkitValidatorVJF.umd.js.map +1 -0
  247. package/umd/MobxFormkitValidatorVJF.umd.min.js +2 -0
  248. package/umd/MobxFormkitValidatorVJF.umd.min.js.map +1 -0
  249. package/umd/MobxFormkitValidatorYUP.umd.js +77 -0
  250. package/umd/MobxFormkitValidatorYUP.umd.js.map +1 -0
  251. package/umd/MobxFormkitValidatorYUP.umd.min.js +2 -0
  252. package/umd/MobxFormkitValidatorYUP.umd.min.js.map +1 -0
  253. package/umd/MobxFormkitValidatorZOD.umd.js +49 -0
  254. package/umd/MobxFormkitValidatorZOD.umd.js.map +1 -0
  255. package/umd/MobxFormkitValidatorZOD.umd.min.js +2 -0
  256. package/umd/MobxFormkitValidatorZOD.umd.min.js.map +1 -0
package/AGENTS.md ADDED
@@ -0,0 +1,761 @@
1
+ # Guide for AI Agents
2
+
3
+ A concise guide for LLMs working with **MobX Formkit** — the reactive MobX form state management library.
4
+
5
+ ---
6
+
7
+ ## 1. Project Identity
8
+
9
+ | Field | Value |
10
+ | ---------------- | -------------------------------------------------------------------------------------- |
11
+ | **Package** | `mobx-formkit` |
12
+ | **NPM** | [npmjs.com/package/mobx-formkit](https://www.npmjs.com/package/mobx-formkit) |
13
+ | **GitHub** | [github.com/foxhound87/mobx-formkit](https://github.com/foxhound87/mobx-formkit) |
14
+ | **Docs** | [foxhound87.github.io/mobx-formkit](https://foxhound87.github.io/mobx-formkit/) |
15
+ | **Version** | 7.x (current: 7.1.0) |
16
+ | **Size** | ~8KB gzip (tree-shakeable) |
17
+ | **License** | MIT |
18
+ | **Dependencies** | MobX 5, 6 & 7 (peer: `^5.15.0 || ^6.0.0 || ^7.0.0`), lodash, React (optional) |
19
+
20
+ ---
21
+
22
+ ## 2. Philosophy
23
+
24
+ MobX Formkit is built on three core principles:
25
+
26
+ 1. **Reactive by default** — Every field is a MobX observable. Form state (value, error, dirty, touched, etc.) is reactive. Your UI updates automatically with `observer()` — no manual `setState`, no change handlers to write.
27
+
28
+ 2. **Composable by design** — Fields nest infinitely (objects, arrays, arrays of arrays). Forms compose via `composer()` for wizards and multi-step flows. `ArrayMap` preserves insertion order for dynamic lists.
29
+
30
+ 3. **Plugin-driven validation** — 8 validation drivers (DVR, VJF, AJV, YUP, JOI, ZOD, VALIBOT, VINEJS) share a uniform lifecycle. Swap or combine them without changing field definitions.
31
+
32
+ The result: you define your form structure declaratively, and reactivity, validation, change tracking, and UI bindings are automatic.
33
+
34
+ ---
35
+
36
+ ## 3. Architecture
37
+
38
+ ```
39
+ ┌──────────────────────────────────────────────────────┐
40
+ │ Form │
41
+ │ entry point — creates fields, runs validation, │
42
+ │ manages form-level computed state (isValid, error) │
43
+ ├──────────────────────────────────────────────────────┤
44
+ │ fields: ArrayMap<Field> validator: Validator │
45
+ │ $hooks / $handlers options: Options │
46
+ │ state: State (initial/current props, struct, etc.) │
47
+ └──────────────────────┬───────────────────────────────┘
48
+ │ creates
49
+ ┌──────────────────────┴───────────────────────────────┐
50
+ │ Field │
51
+ │ wraps each form input — value, validation, events │
52
+ ├──────────────────────────────────────────────────────┤
53
+ │ $value (observable) errorSync / errorAsync │
54
+ │ $focused / $blurred / $touched / $changed │
55
+ │ $label / $placeholder / $disabled / $rules │
56
+ │ $hooks / $handlers $observers / $interceptors │
57
+ │ $converter / $input / $output │
58
+ │ fields: ArrayMap (nested sub-fields) │
59
+ └──────────────────────┬───────────────────────────────┘
60
+ │ shared via Base
61
+ ┌──────────────────────┴───────────────────────────────┐
62
+ │ Base │
63
+ │ $(), get(), set(), validate(), submit(), add(), │
64
+ │ del(), update(), clear(), reset(), each(), map() │
65
+ │ execHook(), execHandler() │
66
+ │ onClear(), onReset(), onSubmit(), onAdd(), onDel() │
67
+ └──────────────────────────────────────────────────────┘
68
+ ```
69
+
70
+ ### Supporting Classes
71
+
72
+ | Class | Role |
73
+ | ---------------- | ---------------------------------------------------------------------------------------------------------------- |
74
+ | **`Validator`** | Runs validation across all enabled drivers. Manages `validate()`, `validateField()`, `validateRelatedFields()`. |
75
+ | **`Options`** | 40+ form-level configuration flags (validation triggers, debounce, strict modes, converters, soft delete, etc.). |
76
+ | **`Bindings`** | Maps field props → component props via rewriters (simple key mapping) or templates (function-based). |
77
+ | **`State`** | Holds initial, current, and struct data. Bridges field → form during initialization. |
78
+ | **`ArrayMap`** | Ordered key-value collection (backed by observable array). Supports `move(from, to)` for sortable lists. |
79
+ | **`composer()`** | Utility function for orchestrating multiple Form instances (wizards, multi-step flows). |
80
+
81
+ ---
82
+
83
+ ## 4. Key Concepts
84
+
85
+ ### 4.1 Form
86
+
87
+ The entry point. Instantiated with field definitions + config:
88
+
89
+ ```javascript
90
+ const form = new Form(
91
+ { fields: [{ name: 'email', label: 'Email', rules: 'required|email' }] },
92
+ { plugins: { dvr: dvr({ package: validatorjs }) }, hooks: { onSuccess(form) { ... } } }
93
+ );
94
+ ```
95
+
96
+ **Constructor signature:**
97
+
98
+ ```
99
+ Form(setup: FieldsDefinitions, config: FormConfig)
100
+ ```
101
+
102
+ | Config key | Type | Purpose |
103
+ | ---------- | --------------------------------------------------- | ------------------------------- | --------------------------- |
104
+ | `plugins` | `{ dvr?, vjf?, ajv?, yup?, zod?, joi?, valibot?, vinejs? }` | Validation drivers |
105
+ | `hooks` | `{ onInit?, onChange?, onSuccess?, onError?, ... }` | Lifecycle callbacks |
106
+ | `handlers` | `{ onSubmit?, onKeyDown?, ... }` | Custom event handlers (curried) |
107
+ | `options` | `OptionsModel` | Form behavior flags |
108
+ | `bindings` | `Record<string, Rewriter | Template>` | Custom bindings for UI libs |
109
+ | `extra` | `Record<string, any>` | Arbitrary metadata |
110
+ | `name` | `string` | Form name for debugging |
111
+
112
+ ### 4.2 Field
113
+
114
+ Represents a single form input. Observable properties drive UI reactivity:
115
+
116
+ **Core props:** `value`, `initial`, `default`, `label`, `placeholder`, `type`, `bindings`, `extra`, `options`, `related`, `rules`, `validators`, `disabled`, `deleted`, `autoFocus`, `inputMode`, `ref`, `nullable`, `autoComplete`
117
+
118
+ **Computed state:** `error`, `isValid`, `isDirty`, `isPristine`, `isEmpty`, `isDefault`, `hasError`, `focused`, `blurred`, `touched`, `validatedValue`, `checked`, `changed`, `validating`, `submitting`, `clearing`, `resetting`, `actionRunning`
119
+
120
+ **Converters:** `$converter` (value transform), `$input` (apply on input), `$output` (apply on output via `values()`)
121
+
122
+ ### 4.3 Nested Fields
123
+
124
+ Fields can contain sub-fields — infinitely nestable. Accessed via dot notation:
125
+
126
+ ```javascript
127
+ // Access
128
+ form.$("address"); // Field (container)
129
+ form.$("address.city"); // Field (nested)
130
+ form.values(); // { address: { city: '...', zip: '...' } }
131
+
132
+ // Arrays
133
+ form.$("members[0]"); // Field
134
+ form.$("members[0].name"); // nested inside array element
135
+ form.$("members[].name"); // applies to all array elements
136
+ ```
137
+
138
+ **For nested fields and arrays of objects, prefer the separated prop mode** — define the structure via `struct` and split props across parallel objects. This keeps definitions clean and avoids deeply nested unified objects:
139
+
140
+ ```javascript
141
+ const fields = {
142
+ struct: ["address.city", "address.zip", "members[].name", "members[].email"],
143
+ labels: {
144
+ "address.city": "City",
145
+ "address.zip": "ZIP Code",
146
+ "members[].name": "Name",
147
+ "members[].email": "Email",
148
+ },
149
+ rules: {
150
+ "address.city": "required",
151
+ "address.zip": "required|digits:5",
152
+ "members[].name": "required",
153
+ },
154
+ values: {
155
+ address: { city: "Rome", zip: "00100" },
156
+ members: [{ name: "John", email: "john@test.com" }],
157
+ },
158
+ };
159
+ ```
160
+
161
+ **ArrayMap** provides ordered key-value storage with array-like operations (`move(from, to)` for sortable lists) while exposing the full Map API.
162
+
163
+ ### 4.4 Computed (Functional) Props
164
+
165
+ Any field prop can be a function that receives `{ field, form }` and returns a reactive value:
166
+
167
+ ```javascript
168
+ const fields = [
169
+ {
170
+ name: "total",
171
+ value: ({ field, form }) =>
172
+ form
173
+ .$("items")
174
+ .values()
175
+ .reduce((sum, item) => sum + item.price * item.qty, 0),
176
+ disabled: ({ field, form }) => !form.$("items").values().length,
177
+ },
178
+ ];
179
+ ```
180
+
181
+ Computed props re-evaluate automatically when their dependencies (MobX observables they read) change. Available on: `value`, `label`, `placeholder`, `disabled`, `rules`, `related`, `bindings`, `extra`, `options`, `deleted`, `autoFocus`, `inputMode`, and all `computed` fields.
182
+
183
+ ### 4.5 Hooks
184
+
185
+ Lifecycle callbacks. You define them in the form config or in field definitions:
186
+
187
+ ```javascript
188
+ const hooks = {
189
+ // Form lifecycle
190
+ onInit(form) { ... }, // After form creation
191
+ onChange(form) { ... }, // On any field value change
192
+
193
+ // Submit lifecycle
194
+ onSuccess(form) { ... }, // Validation passed
195
+ onError(form) { ... }, // Validation failed
196
+ onSubmit(form) { ... }, // Before validation
197
+
198
+ // Field lifecycle
199
+ onChange(field, event) { ... }, // Field value changed
200
+ onBlur(field, event) { ... }, // Field lost focus
201
+ onFocus(field, event) { ... }, // Field gained focus
202
+ onClear(field) { ... }, // Field cleared
203
+ onReset(field) { ... }, // Field reset
204
+ onAdd(field) { ... }, // Field added dynamically
205
+ onDel(field) { ... }, // Field removed dynamically
206
+ };
207
+ ```
208
+
209
+ **Important:** Hooks receive `(field, event)` when triggered by event handlers, but only `(field)` when called directly from actions.
210
+
211
+ ### 4.6 Handlers
212
+
213
+ Event handler functions passed to DOM elements via `field.bind()`. Custom handlers are **curried**: `(field) => (event) => {}`:
214
+
215
+ ```javascript
216
+ const handlers = {
217
+ onKeyDown: (field) => (e) => {
218
+ if (e.key === "Enter") field.submit();
219
+ },
220
+ onSubmit: (form) => (e) => {
221
+ e.preventDefault();
222
+ console.log("submitted");
223
+ },
224
+ };
225
+ ```
226
+
227
+ Built-in handlers on Field: `onChange` (aliased as `onSync`), `onBlur`, `onFocus`, `onKeyDown`, `onKeyUp`, `onToggle`, `onDrop`.
228
+
229
+ Built-in handlers on Form/Base: `onSubmit`, `onClear`, `onReset`, `onAdd`, `onDel`.
230
+
231
+ ### 4.7 Validation Drivers
232
+
233
+ Eight plugins sharing a uniform lifecycle. Enable one or more:
234
+
235
+ ```javascript
236
+ import dvr from "mobx-formkit/lib/validators/DVR";
237
+ import vjf from "mobx-formkit/lib/validators/VJF";
238
+ import yupPlugin from "mobx-formkit/lib/validators/YUP";
239
+ import joiPlugin from "mobx-formkit/lib/validators/JOI";
240
+ import zodPlugin from "mobx-formkit/lib/validators/ZOD";
241
+ import ajv from "mobx-formkit/lib/validators/AJV";
242
+ import valibotPlugin from "mobx-formkit/lib/validators/VALIBOT";
243
+ import vinejsPlugin from "mobx-formkit/lib/validators/VINEJS";
244
+ ```
245
+
246
+ | Driver | Rules Format | Async | Extend | Best For |
247
+ | --------- | ------------------------------------ | ----- | ------ | ------------------------------- |
248
+ | **DVR** | String rules (`'required\|email'`) | ✅ | ✅ | Simple forms, quick setup |
249
+ | **VJF** | Custom functions `() => [bool, msg]` | ✅ | ✅ | Full control, complex logic |
250
+ | **AJV** | JSON Schema | ✅ | ✅ | API compatibility, schema-first |
251
+ | **YUP** | `y.string().required()` | ❌ | ❌ | Modern JS/TS |
252
+ | **JOI** | `j.string().required()` | ❌ | ❌ | Enterprise, rich rules |
253
+ | **ZOD** | `z.string().min(3)` | ❌ | ❌ | TypeScript-native |
254
+ | **VALIBOT** | `v.string(), v.minLength(3)` | ❌ | ✅ | Sync, tiny TS-first schemas |
255
+ | **VINEJS** | `vine.string().minLength(3)` | ✅* | ✅ | Sync rules, async API |
256
+
257
+ \* VineJS `validate()` is async-by-design at the API level (even for synchronous
258
+ rules) — the driver consumes the MRF async contract (`setValidationAsyncData` +
259
+ `promises`), mirroring the YUP driver.
260
+
261
+ - **ZOD** driver supports **zod v3.25+ and v4** (peer `^3.25.0 || ^4.0.0`,
262
+ optional peer; devDep in repo). Driver relies only on `safeParse` + `error.format()`,
263
+ stable across both majors.
264
+
265
+ **Example — enable DVR + VJF together:**
266
+
267
+ ```javascript
268
+ const plugins = {
269
+ dvr: dvr({ package: validatorjs }),
270
+ vjf: vjf(),
271
+ };
272
+ ```
273
+
274
+ Control order via `options.validationPluginsOrder: ['vjf', 'dvr']`.
275
+
276
+ ### 4.8 Bindings
277
+
278
+ `field.bind()` returns props for your UI component. Two strategies:
279
+
280
+ **Default bindings** (text input → `{ id, name, type, value, onChange, onBlur, ... }`):
281
+
282
+ ```jsx
283
+ <input {...form.$("email").bind()} />
284
+ ```
285
+
286
+ **Rewriters** — simple key mapping for UI libraries:
287
+
288
+ ```javascript
289
+ const bindings = {
290
+ MaterialTextField: { label: "floatingLabelText", placeholder: "hintText" },
291
+ };
292
+ // Then: <TextField {...form.$('email').bind()} />
293
+ ```
294
+
295
+ **Templates** — full control with a function:
296
+
297
+ ```javascript
298
+ const bindings = {
299
+ CustomSelect: ({ field, form, keys, props, $try }) => ({
300
+ value: field.value,
301
+ onChange: (e, val) => (field.value = val),
302
+ error: field.error,
303
+ options: field.extra.options,
304
+ }),
305
+ };
306
+ ```
307
+
308
+ ### 4.9 Options
309
+
310
+ 40+ configuration flags. Set via `options` in the form config:
311
+
312
+ | Group | Key Options |
313
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
314
+ | **Validation triggers** | `validateOnInit` (true), `validateOnBlur` (true), `validateOnChange` (false), `validateOnSubmit` (true), `validateOnClear` (false), `validateOnReset` (false) |
315
+ | **Error display** | `showErrorsOnInit` (false), `showErrorsOnBlur` (true), `showErrorsOnChange` (true), `showErrorsOnSubmit` (true), `showErrorsOnClear` (false), `showErrorsOnReset` (true) |
316
+ | **Debounce** | `validationDebounceWait` (250ms), `validationDebounceOptions` `{ leading: false, trailing: true }` |
317
+ | **Strict modes** | `strictSelect` (true), `strictDelete` (true), `strictSet` (false), `strictUpdate` (false) |
318
+ | **Converters** | `applyInputConverterOnInit` (true), `applyInputConverterOnSet` (true), `applyInputConverterOnUpdate` (true) |
319
+ | **Retrieval** | `retrieveOnlyDirtyFieldsValues` (false), `retrieveOnlyEnabledFieldsValues` (false), `softDelete` (false), `retrieveNullifiedEmptyStrings` (false), `removeNullishValuesInArrays` (false) |
320
+ | **Validation** | `stopValidationOnError` (false), `resetValidationBeforeValidate` (true), `validateDisabledFields` (false), `validateDeletedFields` (false), `validatePristineFields` (true), `validationPluginsOrder` (undefined) |
321
+ | **Other** | `submitThrowsError` (true), `defaultGenericError` (null), `fallback` (true), `fallbackValue` (''), `autoTrimValue` (false), `autoParseNumbers` (false), `bubbleUpErrorMessages` (false), `preserveDeletedFieldsValues` (false) |
322
+
323
+ Options can be overridden **per-field** via `field.$options`:
324
+
325
+ ```javascript
326
+ const fields = [
327
+ {
328
+ name: "email",
329
+ options: { validateOnChange: true, validationDebounceWait: 500 },
330
+ },
331
+ ];
332
+ ```
333
+
334
+ ### 4.10 Field Definition Modes
335
+
336
+ **Unified mode** — all props in one object per field:
337
+
338
+ ```javascript
339
+ const fields = [
340
+ {
341
+ name: "email",
342
+ label: "Email",
343
+ rules: "required|email",
344
+ value: "test@test.com",
345
+ },
346
+ ];
347
+ ```
348
+
349
+ **Separated mode** — props split across parallel objects:
350
+
351
+ ```javascript
352
+ const fields = {
353
+ struct: ["email"],
354
+ values: { email: "test@test.com" },
355
+ labels: { email: "Email" },
356
+ rules: { email: "required|email" },
357
+ };
358
+ ```
359
+
360
+ **Mixed mode** — unified overrides separated. Any combination works.
361
+
362
+ ### 4.11 Field Converters
363
+
364
+ Three levels of value transformation:
365
+
366
+ - **`$converter`** — transforms the raw value whenever it's set (e.g., uppercase, trim)
367
+ - **`$input`** — applied when value comes from user input (via `sync`, `onChange`, `update`, `set`)
368
+ - **`$output`** — applied when value is read via `values()`, `flatMapValues`, or `get('value')`
369
+
370
+ Apply via field definition:
371
+
372
+ ```javascript
373
+ {
374
+ name: 'email',
375
+ input: (val) => val.toLowerCase().trim(),
376
+ output: (val) => val ? `***@${val.split('@')[1]}` : '',
377
+ converter: (val) => val?.toUpperCase(),
378
+ }
379
+ ```
380
+
381
+ ---
382
+
383
+ ## 5. Core API Reference
384
+
385
+ ### 5.1 Field Selection
386
+
387
+ ```javascript
388
+ form.$("email"); // typed: Field<EmailType>
389
+ form.$("address.city"); // nested by dot notation
390
+ form.$("members[0].name"); // array by index
391
+ form.$("members[].name"); // returns Field for first element
392
+ form.select("email"); // untyped variant
393
+ form.has("email"); // boolean check
394
+ form.$("email").$("nestedField"); // relative selection from a field
395
+ ```
396
+
397
+ ### 5.2 Getting / Setting Values
398
+
399
+ ```javascript
400
+ // Single
401
+ form.$("email").value; // get
402
+ form.$("email").value = "new@test.com"; // set via setter
403
+ form.$("email").set("new@test.com"); // set via method
404
+ form.set("email", "new@test.com"); // set via form
405
+
406
+ // Bulk
407
+ form.values(); // { email: '...', address: { city: '...' } }
408
+ form.update({ email: "new@test.com", address: { city: "New York" } }); // nested
409
+ form.get("value"); // same as values()
410
+ form.get("error"); // { email: '...', address: { city: '...' } }
411
+ form.errors(); // same
412
+ form.get(["value", "error"]); // { email: { value: '...', error: '...' } }
413
+ form.$("email").get(["value", "error"]); // { value: '...', error: '...' }
414
+
415
+ // Field-level
416
+ field.value; // get
417
+ field.value = "new"; // set
418
+ field.initial; // original value
419
+ field.default; // default value
420
+ field.validatedValue; // value after converters applied
421
+ ```
422
+
423
+ ### 5.3 Validation
424
+
425
+ ```javascript
426
+ // Validate all fields
427
+ await form.validate();
428
+ await form.validate({ showErrors: true, related: true });
429
+
430
+ // Submit (validate + hooks)
431
+ await form.submit(); // calls onSuccess or onError
432
+ await form.submit({ onSuccess, onError }, { validate: true });
433
+
434
+ // Single field
435
+ await form.$("email").validate();
436
+ await form.$("email").validate({ related: true }); // validates related fields too
437
+
438
+ // Manual error
439
+ form.$("email").invalidate("Custom error");
440
+ form.invalidate("Form-level error");
441
+
442
+ // Control error display
443
+ form.showErrors(true); // show all errors
444
+ form.$("email").showErrors(false); // hide specific field error
445
+ form.check(); // check without displaying errors
446
+ ```
447
+
448
+ ### 5.4 Dynamic Fields
449
+
450
+ ```javascript
451
+ form.add({ name: "newField", value: "test" }); // add a field
452
+ form.add({ name: "guest", fields: [{ name: "email" }] }); // add nested field
453
+ form.del("oldField"); // remove a field
454
+ form.$("members").add({ name: "John" }); // add to array container
455
+ form.$("members").del("0"); // remove array element
456
+ form.$("members").move(0, 2); // reorder (sortable lists)
457
+ ```
458
+
459
+ ### 5.5 Clear / Reset
460
+
461
+ ```javascript
462
+ form.clear(); // clear all values + reset validation
463
+ form.reset(); // restore initial/default values
464
+ form.$("email").clear(); // single field
465
+ form.$("email").reset();
466
+ ```
467
+
468
+ ### 5.6 State Checks
469
+
470
+ ```javascript
471
+ form.isValid; // all fields valid (every)
472
+ form.isDirty; // any field changed (some)
473
+ form.isPristine; // no field changed (every)
474
+ form.isEmpty; // all fields empty
475
+ form.hasError; // any field has error (some)
476
+ form.error; // first error message (requires bubbleUpErrorMessages)
477
+ form.focused; // any field focused
478
+ form.touched; // any field blurred at least once
479
+ form.disabled; // all fields disabled (every)
480
+ form.changed; // change count (number)
481
+ form.submitted; // submission count (number)
482
+ form.validating; // validation in progress (boolean)
483
+ form.submitting; // submission in progress (boolean)
484
+ form.size; // number of top-level fields
485
+ ```
486
+
487
+ ### 5.7 Event Wiring
488
+
489
+ ```javascript
490
+ // In JSX — built-in handlers
491
+ <form onSubmit={form.onSubmit}>
492
+ <input {...form.$("email").bind()} />
493
+ <button onClick={form.onClear}>Clear</button>
494
+ <button onClick={form.onReset}>Reset</button>
495
+ </form>
496
+
497
+ // The bind() output includes: id, name, type, value, checked,
498
+ // label, placeholder, disabled, autoComplete, onChange, onBlur,
499
+ // onFocus, autoFocus, inputMode, onKeyUp, onKeyDown
500
+ ```
501
+
502
+ ### 5.8 TypeScript
503
+
504
+ ```typescript
505
+ import { Form, Field } from 'mobx-formkit';
506
+ import type { PathsOf, FieldDefinition } from 'mobx-formkit';
507
+
508
+ // Typed form
509
+ interface Profile {
510
+ username: string;
511
+ email: string;
512
+ address: { city: string; zip: string };
513
+ }
514
+
515
+ const form = new Form<Profile>({ fields: [...] });
516
+ form.$('username'); // Field<string>
517
+ form.$('address.city'); // Field<string> (with PathsOf autocomplete)
518
+
519
+ // Typed field definitions
520
+ const fields: Record<string, FieldDefinition> = [
521
+ { name: 'email', rules: 'required|email' },
522
+ ];
523
+ ```
524
+
525
+ ### 5.9 Composer (Multi-Form)
526
+
527
+ ```javascript
528
+ import { composer } from "mobx-formkit/lib/composer";
529
+
530
+ const wizard = composer({ step1: form1, step2: form2 });
531
+ wizard.validate(); // validates all forms
532
+ wizard.submit(); // submits all forms
533
+ wizard.clear(); // clears all forms
534
+ wizard.valid(); // all forms valid?
535
+ wizard.error(); // any form has error?
536
+ wizard.get("value"); // { step1: {...}, step2: {...} }
537
+ wizard.select("step1"); // individual form
538
+ ```
539
+
540
+ ### 5.10 Observers & Interceptors
541
+
542
+ MobX `observe`/`intercept` on any field prop:
543
+
544
+ ```javascript
545
+ const fields = [{
546
+ name: 'email',
547
+ observers: [{
548
+ prop: 'value',
549
+ call: ({ change, field, form }) =>
550
+ console.log('Email changed:', change.newValue),
551
+ }],
552
+ interceptors: [{
553
+ prop: 'value',
554
+ call: ({ change }) => change.newValue?.length > 50 ? null : change,
555
+ }],
556
+ }];
557
+
558
+ // Or imperatively:
559
+ field.observe(({ change, field, form }) => { ... });
560
+ field.intercept(({ change }) => change);
561
+ form.intercept({ prop: 'value', path: 'email', call: ({ change }) => change });
562
+ ```
563
+
564
+ ---
565
+
566
+ ## 6. Field Definition Reference
567
+
568
+ All properties you can define per field:
569
+
570
+ | Prop | Type | Description |
571
+ | --------------- | -------------------------- | ---------------------------------------------------------------------------- |
572
+ | `name` | `string` | Field name (overrides key) |
573
+ | `value` | `any` | Field value |
574
+ | `computed` | `({ field, form }) => any` | Reactive computed value |
575
+ | `label` | `string \| function` | Display label |
576
+ | `placeholder` | `string \| function` | Placeholder text |
577
+ | `type` | `string` | Input type (`text`, `checkbox`, `password`, `file`, `number`, etc.) |
578
+ | `rules` | `string` | Validation rules (DVR: `'required\|email'`, VJF/AJV/YUP/JOI/ZOD/VALIBOT/VINEJS: per-driver) |
579
+ | `validators` | `function[]` | Custom validation functions (VJF) |
580
+ | `related` | `string[]` | Re-validate these field paths when this field changes |
581
+ | `disabled` | `boolean \| function` | Disabled state |
582
+ | `deleted` | `boolean \| function` | Soft-delete flag (requires `softDelete: true`) |
583
+ | `default` | `any` | Default value (used on reset) |
584
+ | `initial` | `any` | Initial value (set on creation) |
585
+ | `fields` | `array` | Nested field definitions |
586
+ | `bindings` | `string` | Name of binding to use (from `form.bindings`) |
587
+ | `extra` | `any` | Arbitrary metadata |
588
+ | `options` | `OptionsModel` | Per-field option overrides |
589
+ | `hooks` | `object` | Field-level hooks |
590
+ | `handlers` | `object` | Field-level handlers |
591
+ | `autoFocus` | `boolean \| function` | Auto-focus on mount |
592
+ | `inputMode` | `string \| function` | Mobile keyboard mode |
593
+ | `ref` | `any` | Ref callback/object |
594
+ | `nullable` | `boolean` | Allow null value |
595
+ | `autoComplete` | `string` | Autocomplete attribute |
596
+ | `input` | `function` | Input converter (user → internal) |
597
+ | `output` | `function` | Output converter (internal → API) |
598
+ | `converter` | `function` | Transform on set |
599
+ | `observers` | `array` | MobX observers |
600
+ | `interceptors` | `array` | MobX interceptors |
601
+ | `class` | `class` | Custom Field class for this field |
602
+ | `validatedWith` | `string` | Which prop to validate (`'value'` default) |
603
+
604
+ ---
605
+
606
+ ## 7. Best Practices
607
+
608
+ ### Validation
609
+
610
+ - **Prefer hooks over raw handlers** for most validation logic. Define `onSuccess` and `onError` in the form config.
611
+ - **VJF is best for complex logic** — regex, API checks, cross-field rules, async validation.
612
+ - **DVR is best for simple forms** — string rules like `'required|email|between:5,25'` are concise.
613
+ - **Use `related`** to auto-revalidate dependent fields (e.g., `passwordConfirm` when `password` changes).
614
+ - **`validateOnChange` is false by default** — enable it per-field when needed, or use `validateOnBlur` (default: true).
615
+ - **Set `validationDebounceWait`** higher (e.g., 500ms) for expensive async validation.
616
+
617
+ ### Performance
618
+
619
+ - **Use `retrieveOnlyDirtyFieldsValues: true`** to only submit changed fields.
620
+ - **Use `retrieveOnlyEnabledFieldsValues: true`** to exclude disabled fields from API payloads.
621
+ - **Keep forms flat where possible** — deeply nested fields add overhead.
622
+ - **Use `bubbleUpErrorMessages: true`** for a quick `form.error` string.
623
+ - **Lazy validation** — disable `validateOnInit` for forms with many fields that aren't immediately visible.
624
+
625
+ ### Reactivity
626
+
627
+ - **Wrap components with `observer()`** from `mobx-react` to auto-render on field changes.
628
+ - **Computed props** `({ field, form }) => ...` are reactive — use them instead of manual `reaction()`.
629
+ - **Don't spread `field.bind()` deeply** — the spread is reactive as long as the component is observed.
630
+ - **Use `field.validatedValue`** when converters are applied and you need the processed value.
631
+
632
+ ### Structure
633
+
634
+ - **Unified mode** is simplest for flat, simple forms. **For nested fields and arrays of objects, prefer separated mode** — define paths via `struct` and props via parallel objects. This avoids deeply nested unified objects and makes definitions cleaner.
635
+ - **Use `struct`** in separated mode to explicitly define the shape when `fallback: false`.
636
+ - **Dynamic arrays**: use `form.$('items').add({ name: 'newItem' })` and `form.$('items').del('0')`.
637
+ - **Sortable lists**: `form.$('items').move(oldIndex, newIndex)` — integrated with drag-and-drop.
638
+ - **Nested forms**: use `composer()` for independent form instances in wizards.
639
+
640
+ ### TypeScript
641
+
642
+ - **Always pass the data interface** as a generic: `new Form<MyInterface>(...)`.
643
+ - **Use `PathsOf<F>`** for type-safe nested field access.
644
+ - **Define fields with `Record<string, FieldDefinition>`** for autocomplete.
645
+ - **Custom Field classes** can extend `Field` for reusable field behavior.
646
+
647
+ ### Gotchas
648
+
649
+ | Gotcha | Detail |
650
+ | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
651
+ | **Handlers are curried** | Custom handlers: `(field) => (e) => {}`, not `(field, e) => {}`. |
652
+ | **`submitThrowsError: true`** | Default — wrap `submit()` in try/catch or use validation hooks. |
653
+ | **`onSubmit` hook** receives form, not field. `onSuccess`/`onError` receive form. |
654
+ | **`field.bind()`** sets `field.$ref` after render. Access in `useEffect` or similar. |
655
+ | **`softDelete`** requires `softDelete: true` option; deleted fields are excluded from `values()`. |
656
+ | **`fallback: false`** + missing fields from struct won't be auto-created. Useful for strict schemas. |
657
+ | **`strictSelect: true`** (default) throws on invalid field paths — catch or disable for dynamic paths. |
658
+ | **`fallbackValue`** controls the default empty value (`''` by default; can be `null`, `undefined`, `0`). |
659
+
660
+ ---
661
+
662
+ ## 8. Quick Patterns
663
+
664
+ ### Simplest possible form
665
+
666
+ ```javascript
667
+ const form = new Form(
668
+ {
669
+ fields: [{ name: "email", rules: "required|email" }],
670
+ },
671
+ {
672
+ plugins: { dvr: dvr({ package: validatorjs }) },
673
+ hooks: { onSuccess: (f) => console.log(f.values()) },
674
+ },
675
+ );
676
+ ```
677
+
678
+ ### Field with computed value
679
+
680
+ ```javascript
681
+ { name: 'fullName', value: ({ field, form }) =>
682
+ `${form.$('firstName').value} ${form.$('lastName').value}`.trim()
683
+ }
684
+ ```
685
+
686
+ ### Custom error handling
687
+
688
+ ```javascript
689
+ try {
690
+ await form.submit();
691
+ } catch {
692
+ console.log("Validation failed:", form.errors());
693
+ }
694
+ ```
695
+
696
+ ### Per-field validation on change
697
+
698
+ ```javascript
699
+ { name: 'email', options: { validateOnChange: true } }
700
+ ```
701
+
702
+ ### Async validation (VJF)
703
+
704
+ ```javascript
705
+ { name: 'username', validators: [({ field }) =>
706
+ fetch(`/api/check-username?q=${field.value}`)
707
+ .then(r => r.json())
708
+ .then(({ available }) => [available, 'Username taken'])
709
+ ]}
710
+ ```
711
+
712
+ ### Array with dynamic items
713
+
714
+ ```javascript
715
+ const fields = {
716
+ struct: ["items[].name", "items[].qty"],
717
+ rules: {
718
+ "items[].name": "required",
719
+ "items[].qty": "required|numeric",
720
+ },
721
+ };
722
+ // Add: form.$('items').add({ name: 'New', qty: 1 })
723
+ // Remove: form.$('items').del('0')
724
+ ```
725
+
726
+ ### Custom binding for MUI TextField
727
+
728
+ ```javascript
729
+ const bindings = {
730
+ MuiTextField: {
731
+ id: "id",
732
+ name: "name",
733
+ type: "type",
734
+ value: "value",
735
+ label: "label",
736
+ placeholder: "placeholder",
737
+ disabled: "disabled",
738
+ autoFocus: "autoFocus",
739
+ onChange: "onChange",
740
+ onBlur: "onBlur",
741
+ onFocus: "onFocus",
742
+ error: "error",
743
+ helperText: "error", // MUI specific
744
+ },
745
+ };
746
+ ```
747
+
748
+ ---
749
+
750
+ ## 9. Related Repositories
751
+
752
+ | Repo | Purpose |
753
+ | ---------------------------------------------------------------------------------- | ---------------------------------------------------------- |
754
+ | [mobx-formkit-demo](https://github.com/foxhound87/mobx-formkit-demo) | Live demo app with React components |
755
+ | [mobx-formkit-devtools](https://github.com/foxhound87/mobx-formkit-devtools) | DevTools panel for debugging |
756
+ | [mobx-formkit--docs](https://foxhound87.github.io/mobx-formkit/) | This documentation site |
757
+ | [skills](https://github.com/foxhound87/skills) | AI skill files for Cursor, Windsurf, Claude Code, Codebuff |
758
+
759
+ ---
760
+
761
+ > **For LLMs:** When helping users with mobx-formkit, always check which validation plugin is being used — patterns differ significantly between DVR/VJF/AJV/YUP/JOI/ZOD/VALIBOT/VINEJS. Prefer `computed` props over manual reactivity. Always wrap components with `observer()`. Use `field.bind()` instead of manual prop wiring. Custom handlers are **curried** `(field) => (e) =>`.