API Overview
Core Factory
createForm<T>(config) is the single entry point for the library. It is a closure factory — no classes, no singletons, no global state. Each call returns an isolated form instance whose entire state (values, errors, touched, dirty, isSubmitting, isValidating) lives inside the closure. The generic type parameter T constrains initialValues and flows through to all method signatures so form.get('some.path') is fully typed.
See Core API for the full FormConfig and method reference.
Subscription Model
The engine exposes two subscription primitives:
form.subscribe(fn)— global subscriber.fnreceives a completeFormState<T>snapshot on every state mutation. Use this when a component needs to react to the whole form (e.g. a submit button that reflectsisSubmitting).form.subscribeToPath(path, fn)— path-level subscriber.fnreceives(value, fieldState)wherefieldStatecontains{ error, touched, dirty }for that path alone. Mutations to unrelated paths do not trigger this callback, making it ideal for individual field components.
Both return an unsubscribe function. Framework adapters call this automatically on component teardown.
Validation Lifecycle
Validation is always initiated by form.validate(paths?) or implicitly by form.set(path, value, { validate: true }). The connect() bridge also triggers validation automatically based on the form's validationMode (default 'onTouched') — call form.getFieldMode(path) from a framework adapter component to query the effective mode for a specific path. The engine:
- Expands
pathsinto a full scope using the pre-computed dependency graph. - Aborts any in-flight
AbortControllerfor those paths. - Waits for
asyncDebounceMs(default 300 ms) before calling the validator. - Increments an internal epoch counter; stale results arriving after a newer call has started are discarded.
- Merges returned errors into the
errorsmap and notifies subscribers.
See Validation Adapters for schema library integrations and Async Validation for a deep dive on the epoch and abort mechanics.
DOM Bridge
form.connect(path, element, options?) registers an HTMLElement under a form field path using a WeakRef. A lazy MutationObserver on document.body monitors node removals; when an element leaves the DOM its registry entry is pruned and its field state is cleared — unless the path was connected with persist: true, in which case the value is retained.
form.getPayload() returns a partial values object containing only the paths that are currently connected or persisted. This makes it straightforward to collect only the data belonging to the currently-visible step of a multi-step form.
See DOM Connect Bridge for full usage.
Array Operations
createForm exposes five array mutation methods — arrayAppend, arrayInsert, arrayRemove, arrayMove, arraySwap — that operate inside an internal batch() call. Beyond mutating the values array, they remap the errors, touched, and dirty maps so that field state follows the item as it moves rather than staying attached to the old index.
See Array Operations for signatures and examples.