Form object
FormObject turns an HTMLFormElement into a structured plain object β and writes one back β using the control name to shape the result. It is a static snapshot, like new FormData(), with no reactivity or dependencies. Type into the form below and watch the object update.
Field names shape the object
The name attribute is a path. Dots nest objects, numeric segments build arrays, and a trailing [] marks an append array.
import { FormObject } from "elements-kit/utilities/form-object";
// <input name="user.name"> β { user: { name } }// <input name="user.address.city">β { user: { address: { city } } }// <input name="tags.0"> β { tags: [ "β¦" ] } (explicit index)// <input name="colors[]"> β { colors: [ "β¦" ] } (append, one per control)const data = new FormObject(form).toObject();A trailing [] always produces an array β a single colors[] control gives ["x"], and a group with none checked still serializes to []. Use it whenever a field is conceptually a list.
Reading a form
toObject() walks the formβs named controls and returns the nested object. It mirrors native form submission: disabled and unchecked controls are dropped, <select multiple> yields an array, and <input type="file"> yields File objects. toJSON() is an alias, so JSON.stringify(instance) works.
const form = document.querySelector("form")!;const data = new FormObject(form).toObject();Writing back
fromObject(data) assigns values from a nested object onto matching controls β setting .value, checking boxes and radios, and selecting options. Missing paths are left untouched. clear() resets every named control. Both return this for chaining.
new FormObject(form).fromObject({ user: { name: "Wael" }, colors: ["red", "blue"], // checks the matching colors[] boxes});Transforms
Extraction runs each field through a pipeline of FormFieldTransforms. Each transform receives the field ({ control, name, value, checked? }) and returns it, a modified copy, or null to drop it. The default pipeline skips disabled controls, buttons, and unchecked boxes:
import { FormObject, defaultTransforms, uncheckedAs, skipEmpty,} from "elements-kit/utilities/form-object";
// Replace the defaults β include unchecked checkboxes as `false`new FormObject(form, { transforms: [...defaultTransforms.slice(0, 2), uncheckedAs(false)],}).toObject();
// Extend β drop empty fields and trim stringsnew FormObject(form, { transforms: [ ...defaultTransforms, skipEmpty, (f) => ({ ...f, value: typeof f.value === "string" ? f.value.trim() : f.value }), ],}).toObject();Built-ins: skipDisabled, skipButtons, dropUnchecked (the defaults), plus uncheckedAs(value) and skipEmpty. Passing transforms replaces the defaults β spread ...defaultTransforms to keep them.
Field names that walk an objectβs prototype (__proto__, prototype, constructor) are ignored, so a malicious name cannot pollute Object.prototype.
See also
- Utilities
- Signals
- Source & full behavior table: src/utilities/form-object.ts