ElementInternals: Making Custom Elements Form-Friendly

ElementInternals lets your custom elements act like native form inputs, integrating with validation, submission, and labels. Use it for custom inputs in a design system. The footgun: forgetting to call setFormValue() means its state won't be submitted.
Why it exists
Before ElementInternals, custom elements were isolated islands. You could build a beautiful custom input, but getting it to work inside a <form>—to be validated, have its value submitted, or be associated with a <label>—required complex, brittle JavaScript workarounds. ElementInternals was created to solve this, providing a standard API for custom elements to become first-class citizens of HTML forms.
The mental model
Think of ElementInternals as a backstage pass for your custom element. Normally, only native elements like <input> and <select> are allowed in the "form internals" area. By calling attachInternals(), your custom element gets this pass, allowing it to talk to the parent <form>, report its validity status, set its value for submission, and even expose its state to CSS for styling.
How it works
Inside your custom element's class constructor, you call this.attachInternals(). This returns an ElementInternals object, which you store as a property (e.g., this._internals). This object has methods and properties to manage the element's role within a form. For example, you use this._internals.setValidity() to report validation errors and this._internals.setFormValue() to set the value that gets submitted. It also exposes properties for setting default ARIA attributes, like this._internals.role = 'checkbox'.
When to use it
Use ElementInternals whenever you're creating a custom element that should behave like a form control. This is a must-have for building robust, reusable components in a design system, such as custom-styled checkboxes, radio buttons, or complex input fields with unique validation logic. It's also the standard way to manage custom pseudo-class states for styling (e.g., :--loading).
When not to use it
Don't use it for presentational components that don't collect user data or have a state relevant to a form. For example, a custom <card> or <icon> component doesn't need form integration. Using ElementInternals where it's not needed adds unnecessary complexity; it is specifically for elements that need to act like form controls.
One canonical example
For a custom checkbox, you'd call this.attachInternals() in the constructor. When the user clicks it, you'd toggle its state, call this._internals.setFormValue(this.checked ? 'on' : null), and perhaps this._internals.states.toggle('--checked', this.checked). You would also set this._internals.role = 'checkbox' and this._internals.ariaChecked = this.checked.toString() to ensure it's accessible. Now it works in a form just like a native checkbox.
Interview question
What is the primary purpose of using ElementInternals for a custom element?
- a.To facilitate communication between different custom elements.
- b.To enable custom pseudo-class styling for any component.
- c.To integrate the custom element with HTML forms for validation and submission.Correct
- d.To manage complex internal state and lifecycle hooks.
Why? this is the answer
ElementInternals is specifically designed to allow custom elements to behave like native form controls, enabling them to participate in form validation, submission, and accessibility. While it can expose custom pseudo-class states for styling, that is a secondary benefit, not its primary purpose for any component.
Just read this? Test yourself on what you have been reading.
Read the original → developer.mozilla.org
- #web components
- #forms
- #accessibility
- #javascript
- #dom
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Every open role lists the topics its interview covers, so you can prepare for the real thing rather than guessing.
See open roles