Skip to content

Form-Associated Elements

The Form-Associated Custom Elements API lets a custom element participate in a <form> exactly like a native input. The element reports its value, validity, and can be included in FormData and form.submit().

Without this API, a custom element placed inside a <form> is invisible to the browser’s form submission machinery. Values it holds are not serialised, and built-in validation does not apply to it. With the API, your element becomes a first-class form participant indistinguishable from a native <input>.

Two things are required: a static formAssociated = true property on the class, and a call to this.attachInternals() in the constructor.

class MyInput extends HTMLElement {
static formAssociated = true;
constructor() {
super();
this.internals = this.attachInternals();
}
}

The static property must be declared before attachInternals() is called — the browser reads it at construction time to decide whether to grant form-participation abilities.

Once you have the internals object, these are the key form-related methods:

  • internals.setFormValue(value) — sets the value included in FormData when the form is submitted.
  • internals.setValidity(flags, message, anchor) — controls the element’s validation state. Pass an empty object and an empty string to clear all errors.
  • internals.reportValidity() — triggers the browser’s built-in validation UI (the tooltip bubble), the same way a native input would.
  • internals.checkValidity() — returns a boolean indicating whether the element currently passes validation.

The internals.role and ARIA reflection properties are covered in the Accessibility lesson.

// Form-Associated Custom Elements API (Chrome 77+, Firefox 98+, Safari 16.4+)
// static formAssociated = true — must be on the class before attachInternals()
// this.internals = this.attachInternals()
//
// internals.setFormValue(value) — submittable value
// internals.setFormValue(value, state) — value + restore-state hint
// internals.setValidity({}, '') — clear validity errors
// internals.setValidity({ valueMissing: true }, 'Required', anchorEl)
// internals.reportValidity() — show browser validation UI
// internals.checkValidity() — boolean
// internals.form — the associated <form> element
// internals.labels — NodeList of associated <label>s
// internals.willValidate — boolean

The demo below feature-detects support for attachInternals and formAssociated. If the browser does not support them, a fallback message is shown instead.

Type into the custom input and watch the paragraph below the form reflect the value that would be submitted with the form.

What static property must be set to true to make a custom element form-associated?
Which method sets the value that will appear in FormData on form submit?
What should a form-associated element do when attachInternals is not available?