Skip to content

Core concepts

Terminology and architecture for @jayoncode/form-intelligence. Prefer this page for what things mean; feature how-tos live under Guides.

Previous: Tutorial · Next: Capabilities

Glossary

TermMeaning
Form instanceObject returned by createForm() — owns values, errors, submit, workflow
bind()Headless field contract (value, onChange, onBlur, …) for any UI
ValidatorSync/async check returning true or an error string
when() ruleDeclarative show/hide/require/populate/disableSubmit from field values
FormatDisplay ↔ store masking (format / parse) — import helpers from /format
TransformInbound canonicalize before validation (trim → normalize → sanitize → …)
AutosaveDebounced persist of current values (often to an API)
DraftLocal (or custom) restore of in-progress values across refresh
WizardMulti-step flow with per-step fields and validation

Problem → approach

Without a form orchestration layerWith Form Intelligence
Validation, submit guards, and autosave spread across useEffect and handlersOne createForm() instance owns the workflow
Field state duplicated between UI and storeform.state is the single source of truth
Framework-specific hooks lock you to one rendererfield().bind() is headless; adapters are optional
Double-submit and in-flight race conditions handled ad hocsubmit() + isSubmitting() built in

Form instance

createForm(options) returns one instance per logical form. Options accept either:

  • target + schema — enhance existing DOM (<form> or container selector)
  • initialValues + validators — headless/programmatic wiring

Imperative API (no subscription)

Read and act on the form directly:

ts
const form = createForm({ initialValues: { email: "" }, onSubmit });

await form.validate();
await form.submit();
form.reset();

form.state.values;
form.state.errors;
form.state.isValid;
form.isValid();
form.isSubmitting();
form.getValues();
form.getErrors();

No subscribe() required for one-off reads or imperative scripts.

Reactive API (vanilla JS only)

When you render UI manually, subscribe to changes:

ts
form.subscribe(() => {
  render(form.state);
});

Or pass subscribe on createForm (one listener or an array) — same store, invoked once after create, then on every notify, until destroy():

ts
createForm({
  initialValues: { email: "" },
  subscribe: (form) => render(form.state),
});

Framework adapters (React, Vue, Angular) call subscribe() internally — application code should not.

StateAccess
Valuesform.state.values, form.getValues()
Field errorsform.state.errors, form.getErrors()
Meta flagsform.state.isValid, form.isSubmitting()
React UIuseForm()form.state (auto-subscribed)

DOM enhancement

When target or form is provided (or you attach later with form.ref / form.form()), the engine:

  • discovers fields by name
  • wires input / change / blur without manual handlers
  • imports Phase 1 HTML constraint attributes into validators once on attach (see Validation → HTML constraints)
  • surfaces validation errors with aria-invalid and live regions
  • sets novalidate and disables submit buttons while isSubmitting (aria-busy)

This is the same philosophy as Browser Lifecycle: enhance what you already have instead of replacing your markup.

Markup the engine expects

html
<form id="signup">
  <label>
    Email
    <input name="email" type="email" />
  </label>

  <!-- Conditional fields: wrap so show/hide can target the group -->
  <div data-form-intelligent-field="companyName">
    <label>
      Company
      <input name="companyName" />
    </label>
  </div>

  <button type="submit">Continue</button>
</form>

At runtime the form may receive data-form-intelligent="{id}", inputs aria-invalid / data-form-intelligent-validating, and error nodes data-form-intelligent-error="{path}".

API map

ConceptResponsibilityAPI
FieldPath in the value treeform.field("email")
BindingHeadless input contractfield().bind()
Validatortrue or error messagevalidators: { path: [fn] }
SubmitAsync handler when validonSubmit
WorkflowAutosave, draft, wizardworkflow: { … }
RulesShow/hide/require/enablewhen("field").equals(…)

Conditional rules

when() expresses show / hide / require / enable / populate without useEffect. Results land in form.state.fieldUi and form.state.ui.

ts
rules: [when("customerType").equals("Business").show("companyName")],

How-to, import choices, and DOM wrappers: Rules guide. Capability map: Capabilities.

Field meta flags

FlagSet whenTypical use
touchedBlur after focusDefer error display
dirtyValue ≠ initialUnsaved indicator
visitedFocus receivedAnalytics, help text

Next steps

GoalGuide
Feature map / roadmapCapabilities
ValidationValidation
Submit lifecycleSubmission
Conditional logicRules
Snapshots / undoState
Autosave / wizardWorkflow

Inspect live state: State explorer

JOC — headless @jayoncode/* TypeScript libraries. Docs sync from package source via TypeDoc and sync scripts.