Skip to content

Form Intelligent

Headless form workflow engine — validation, submission, state, and multi-step orchestration without UI coupling.

Install

bash
npm install @jayoncode/form-intelligent
bash
pnpm add @jayoncode/form-intelligent
bash
yarn add @jayoncode/form-intelligent

React apps also need the adapter:

bash
npm install @jayoncode/form-intelligent @jayoncode/form-intelligent-react

Example: native HTML signup

Enhance existing markup — no React, no manual onChange:

ts
import { createForm } from "@jayoncode/form-intelligent";

createForm({
  target: "#signup",
  schema: { email: "email", name: { required: true, minLength: 2 } },
  validateOn: "onBlur",
  async onSubmit(values) {
    const res = await fetch("/api/signup", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(values),
    });
    if (!res.ok) throw new Error("Signup failed");
  },
});
html
<form id="signup">
  <input name="email" />
  <input name="name" />
  <button type="submit">Sign up</button>
</form>

Try in the playground →

Example: headless signup with async submit

Programmatic wiring when you own the input layer:

ts
import { createForm, email, required } from "@jayoncode/form-intelligent";

const form = createForm({
  initialValues: { email: "", name: "" },
  validators: {
    email: [required, email],
    name: [required],
  },
  validateOn: "onBlur",
  onSubmit: async (values) => {
    const res = await fetch("/api/signup", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(values),
    });
    if (!res.ok) throw new Error("Signup failed");
  },
});

// Headless binding — pass to any input implementation
const emailField = form.field("email").bind();

// Imperative read — no subscription needed
console.log(form.state.values, form.isValid());

// Vanilla JS manual render — subscribe only when you own the UI layer
form.subscribe(() => {
  const { values, errors, isSubmitting, isValid } = form.state;
  renderForm({ values, errors, isSubmitting, canSubmit: isValid && !isSubmitting });
});

await form.submit();

The instance owns values, per-field errors, touched/dirty flags, and submit lifecycle (isSubmitting, duplicate-submit guard). UI layers read state and call bind() handlers — the package does not render components.

Run this flow in the playground →

Problem → approach

Typical painForm Intelligent
Validation, touched state, and submit guards duplicated in every form componentOne createForm() instance owns values, errors, dirty/touched, and submit lifecycle
onSubmit handlers mix fetch, error mapping, and UI flagsonSubmit is async-first; isSubmitting and duplicate-submit guard are built in
Framework form libs own too much UI or too little workflowHeadless bind() + form.state; subscribe() only for manual UI

Overview

createForm() returns a form instance: a single orchestration boundary for field state, validation timing, submission, and optional workflow (autosave, drafts, wizards).

LayerResponsibility
Field APIPath-based accessors, bind() for headless wiring
ValidationSync/async validators, per-field or form-level timing
SubmissiononSubmit, cancel, retry, offline queue
StateSnapshots, field meta, undo/redo, diffs
Ruleswhen() show/hide/require/populate/disableSubmit
WorkflowAutosave debounce, draft persistence, wizard steps
ExtensionPlugins and adapter interfaces for framework bridges

Form Intelligent complements field-registration libraries (React Hook Form, etc.): it focuses on workflow orchestration, not component libraries.

Documentation path

Work through the journey groups below. Each guide links to a playground route.

Getting Started

GuideTopicsPlayground
TutorialcreateForm, bind, submitDashboard

Core Concepts

GuideTopicsPlayground
Core conceptsInstance model, flags, architectureState
CapabilitiesForm OS feature map and statusDashboard

Guides

GuideTopicsPlayground
ValidationBuilt-in rules, schema, asyncValidation
SubmissionLoading, cancel, retries, offlineSubmission
StateSnapshots, meta, undo/redo, diffsState
WorkflowAutosave, drafts, wizardWorkflow
Ruleswhen() show/require/populateRules
CalculationsDerived fieldsCalculations
FormattersParse/format pipelinesFormatters

Integrations

GuideTopicsPlayground
IntegrationsSession, keyboard, analyticsIntegrations
AdaptersFramework + schema bridgesAdapters

Advanced and Support

GuideTopicsPlayground
PluginsLifecycle hooksPlugins
PatternsWizard, offline, recipesDashboard
MigrationFrom DIY autosaveWorkflow

Package fit

RequirementApproach
Debounced autosave / draft restoreworkflow.autosave, workflow.draft
Multi-step flows with per-step validationworkflow.wizard
Conditional show / require / populatewhen() rules — Rules
Derived totalsform.calculate()Calculations
Safe async submit with cancel / retrysubmit() + cancelSubmit() + retry
Offline submit queueworkflow.offlineQueue
Framework-agnostic or custom UIfield().bind()
Declarative field lists onlyConsider an adapter or a field-registration library alongside this package

Reference

JOC is a JayOnCode monorepo of @jayoncode/* packages. Docs sync from source via TypeDoc and sync scripts.