Patterns
Common Form Intelligence recipes — wizard, autosave, offline submit, plugins.
Previous: Plugins · Next: Migration
Playground
Try Workflow, Submission, HTML constraints, and Plugins.
Import path
Recipes below use @jayoncode/form-intelligence unless noted. Formatters → /format; lifecycle plugins → /plugins; schema packages → @jayoncode/form-intelligence-zod (etc.). Entrypoints.
Multi-step wizard
const form = createForm({
initialValues: { email: "", plan: "", card: "" },
validators: {
email: [required, email],
plan: [required],
card: [required],
},
workflow: {
wizard: {
initialStep: 0,
steps: [
{ id: "account", fields: ["email"] },
{ id: "plan", fields: ["plan"] },
{ id: "payment", fields: ["card"] },
],
},
},
onSubmit: async (values) => api.checkout(values),
});
await form.workflow.next(); // validates current step fields
form.workflow.prev();
await form.workflow.goTo(2);
form.state.workflow.currentStep;
form.state.workflow.progress; // 0–100Autosave + draft restore
createForm({
initialValues: { body: "" },
workflow: {
autosave: {
enabled: true,
debounceMs: 400,
onSave: (values) => api.patchDraft(values),
},
draft: {
enabled: true,
storageKey: "editor:draft",
},
},
});Status: form.state.workflow.isAutosaving, form.state.workflow.lastAutosaveAt.
Manual save: form.saveDraft().
Offline submit queue
createForm({
initialValues: { email: "" },
workflow: {
offlineQueue: {
enabled: true,
storageKey: "signup:offline",
},
},
onSubmit: async (values) => api.register(values),
});
// When back online:
await form.flushOfflineQueue();Pair with createBrowserLifecyclePlugin({ flushOfflineQueueOnOnline: true }).
Plugin hooks
createForm({
initialValues: { email: "" },
plugins: [
{
name: "audit",
setup(_form, api) {
api.on("beforeSubmit", () => {
if (!window.confirm("Submit?")) return false;
});
api.on("afterValidate", ({ valid }) => {
console.log("valid?", valid);
});
return {
onDestroy() {
console.log("cleanup");
},
};
},
},
],
});
// Or later: form.use({ name: "audit", setup(...) { ... } });Hooks: beforeValidate, afterValidate, beforeSubmit, afterSubmit, onAutosave, onDraftRestore. Full authoring rules (ownership, engines, testing): Plugins — author guide.
Schema adapters (Zod)
import { zodAdapter } from "@jayoncode/form-intelligence-zod";
import { z } from "zod";
createForm({
initialValues: { email: "" },
schema: zodAdapter(z.object({ email: z.string().email() })),
onSubmit,
});Core stays free of Zod — adapters implement SchemaAdapter.
Headless HTML progressive enhancement
createForm({
target: "#register",
// schema optional — HTML required / type="email" / minlength also import as validators
schema: { email: "email", password: "password" },
onSubmit: async (values) => api.register(values),
});<form id="register">
<input name="email" required type="email" />
<input name="password" required minlength="8" type="password" />
<button type="submit">Register</button>
</form>Phase 1 attributes become FI validators on attach (Field > Schema > HTML). Errors announce with role="alert" and aria-invalid automatically (novalidate). Lab: HTML constraints.
Done? Browse the API Reference or open the playground.
