Skip to content

Core concepts

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

Previous: Tutorial · Next: Capabilities

Problem → approach

Without a form orchestration layerWith Form Intelligent
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);
});

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, the engine:

  • discovers fields by name
  • wires input / change / blur without manual handlers
  • surfaces validation errors with aria-invalid and live regions
  • 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 is a JayOnCode monorepo of @jayoncode/* packages. Docs sync from source via TypeDoc and sync scripts.