The Salesforce Developers website will undergo maintenance on May 29, 2024 from 3:00 a.m. UTC to 10:00 a.m. UTC. The maintenance process may affect the availability of our documentation. Please plan accordingly.

Build Custom Forms in Native Shadow DOM

Available in LWC API v61.0 and later

When working with Salesforce data, you can use the lightning-record-form, lightning-record-edit, or lightning-record-view base components to easily provide a form-based UI for data input or display. You can also build a custom UI using the Lightning Data Service wire adapters.

ElementInternals applies to light DOM and native shadow only. Synthetic shadow supports form association without the use of ElementInternals. Components in synthetic shadow must add ARIA attributes and roles explicitly. To create a form with built-in accessibility in synthetic shadow, like for Lightning Experience and Experience Builder sites, we recommend using the base Lightning components.

Input elements inside a native shadow form are hidden inside the shadow DOM. The form can't validate the input elements for submission, so their data isn't included in the FormData object. Furthermore, form submission fails because the submit event can't escape the shadow boundary.

To include components or elements using native shadow during form submission, use the ElementInternals Web API and the related Form-Associated Custom Elements (FACE) lifecycle API.

Using ElementInternals, you can manage a Lightning web component's internal states, such as default Accessible Rich Internet Applications (ARIA) role or ARIA label. You can also configure ElementInternals to have your component participate in form submissions and validations.

ElementInternals and the FACE lifecycle API provide several key functionalities.

  • Default ARIA semantics-A Lightning web component can have default ARIA attributes and roles even without an explicit ARIA attribute on the host.
  • Form association-A Lightning web component can communicate its form-based states and use lifecycle callbacks corresponding to the form. Call this.attachInternals to associate an ElementInternals object to your component.

Use ElementInternals with components that extend LightningElement only.

ElementInternals provides a way to allow Lightning web components to fully participate in HTML forms. You can assign the internals to a property and define your ARIA role and attribute values like this.

this.attachInternals() is available at any point in the component lifecycle, similar to this.template. However, you can't access internals from outside a component.

this.attachInternals() can be called once only, and throws an error if called more than once. Avoid calling this.attachInternals() in a lifecycle hook that can be called multiple times, such as in connectedCallback or renderedCallback.

To use the lifecycle callbacks, add a formAssociated property to identify the element as a form control. These callbacks are optional and lets your element perform something at that point in the lifecycle.

For more information, see Form-associated custom elements lifecycle callbacks

To use the component that defines the lifecycle callbacks, nest it within a <form> tag.

Similar to the behavior of the LWC connectedCallback lifecycle hook, you can't access formAssociated or the FACE lifecycle callbacks outside a component.

  • ElementInternals is available only with Chromium, Firefox, and Safari. this.attachInternals() throws an error in unsupported browsers. LWC doesn’t provide a polyfill for unsupported browsers.
  • this.attachInternals is undefined in server-side rendering. ElementInternals and the FACE lifecycle callbacks aren't currently serializable as HTML.
  • internals.shadowRoot doesn't return an instance of the ShadowRoot; it returns the same proxy object as this.template. For example, this.template === internals.shadowRoot returns true.

This example creates a custom form using the <input> element and implements the validity states for them. The c-control component contains the <input> element.

When you click the input field, checkValidity() displays a custom error if the field is required and nothing is entered.

The CSS appends a red border when the field is in an invalid state.

Next, use the c-control component within a form element.

The form element contains several buttons, which either resets the form, toggles the disabled property on the input fields, or submits the form.

See Also