npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@lit-labs/forms

v0.1.0

Published

Form helpers for Lit

Readme

@lit-labs/forms

A library for building form-associated custom elements with Lit.

Build Status Published on npm Join our Discord

[!WARNING]

This package is part of Lit Labs. It is published in order to get feedback on the design and may receive breaking changes or stop being supported.

Please read our Lit Labs documentation before using this library in production.

Installation

From inside your project folder, run:

$ npm install @lit-labs/forms

Form-associated custom elements are web components that participate in form submission, validation, and state restoration, just like built-in form elements. @lit-labs/forms helps builds correct form-associated custom elements by setting the proper ARIA roles, form value, disabled state, and more.

This package provides:

  • FormControl: A class mixin that makes it easy to build form associated custom elements that implement the standard form control APIs.
  • FormAssociated: A lower-level class mixin that only helps with form association, but doesn't add any public APIs.
  • Decorators: Decorators for binding properties to form values and state.

FormControl mixin

The FormControl mixin adds standard form-related public API properties and methods to a custom element. It also enables the use of decorators that let developers easily bind class fields to the form value and manage form state for restoration.

[!NOTE]

FormControl extends the FormAssociated mixin, which handles the ElementInternals integration and enables the decorators, but doesn't add any public API to the class.

It's recommended that most developers use FormControl instead of FormAssociated in order to add the idiomatic API that users expect from form controls (like .disabled, .form, .checkValidity(), etc.). Use FormAssociated only if you need to implement a completely custom API surface.

Example

import {LitElement, html} from 'lit';
import {customElement} from 'lit/decorators.js';
import {FormControl, formValue} from '@lit-labs/forms';

@customElement('simple-input')
export class SimpleInput extends FormControl(LitElement) {
  @formValue()
  accessor value = '';

  override render() {
    return html`
      <input
        .value=${this.value}
        @input=${(e: Event) =>
          (this.value = (e.target as HTMLInputElement).value)}
      />
    `;
  }
}

The added fields and methods mimic native form controls, providing a familiar API:

  • form: a readonly property that returns the associated form element.
  • labels: a readonly property that returns the labels associated with the element.
  • name: a read/write property that reflects the name attribute.
  • disabled: a read/write property that reflects the disabled attribute.
  • validity: a readonly property that returns the ValidityState of the element.
  • validationMessage: a readonly property that returns the error message that would be shown to the user if the element was to be checked for validity.
  • willValidate: a readonly property that returns true if internals' target element will be validated when the form is submitted. For example, disabled or hidden elements are not validated.
  • checkValidity(): If the element is invalid, returns false and fires an invalid event.
  • reportValidity(): Like checkValidity() but also shows the validation message to the user.

FormAssociated mixin

The FormAssociated mixin helps define a form-associated custom element.

FormAssociated creates a new form-associated base class and implements a number of best-practice behaviors for form-associated elements:

  • Sets the element's ARIA role using internals.role.
  • Updates the element's form value and state using internals.setFormState().
  • Saves the initial form value and state for use with form reset and restore.
  • Implements form reset via formResetCallback().
  • Implements form state restoration via formStateRestoreCallback().
  • Syncs the disabled state to internals.ariaDisabled via formDisabledCallback().
  • Calls the element's custom validator method, _getValidity(), when the form value changes and updates internals.setValidity() with the result.

FormAssociated doesn't add any public API to the element. Its behavior can be controlled with the decorators described below.

Decorators

@formValue()

The form value can be stored in any class field decorated with @formValue.

  @formValue()
  accessor value = '';

By default, the field must be of type string | File | FormData | null. If your element has a value of a different type, you can use a custom form value converter:

  @formValue({
    converter: {
      toFormValue(value: number) {
        return String(value);
      },
      fromFormValue(value: string) {
        return Number(value);
      },
    },
  })
  accessor value = 23;

@formStateGetter(), @formStateSetter(), and @formState()

Form state is additional data stored by the browser alongside the form value. While the form value is what gets submitted to the server, the form state is used to restore the element's user interface when the user navigates back or forward, or when the browser restores a session.

The underlying browser API is ElementInternals.setFormValue(value, state). The FormAssociated mixin automatically calls this method whenever the @formValue property changes. If you provide form state, it is passed as the second argument.

When the browser restores the element, FormAssociated receives the stored state and uses it to reset the element's properties.

To provide this state, you can use the @formState decorator on a property, or @formStateGetter and @formStateSetter on methods.

The @formState() decorator marks a field as being part of the form state. Its value will be included in the state passed to internals.setFormValue().

@formStateGetter() and @formStateSetter() allow for more complex state handling. They are applied to methods called to get or set the form state. The @formStateGetter() decorated method is called to retrieve the state object to pass to internals.setFormValue(). The @formStateSetter() decorated method is called with the restored state object when the browser restores the element.

class CustomStateElement extends FormAssociated(LitElement) {
  @formValue()
  accessor value = 'bar';

  @formState()
  @property()
  accessor count = 0;

  @formStateGetter()
  // @ts-expect-error #getFormState is called dynamically
  #getFormState() {
    return this.value + '#' + this.count;
  }

  @formStateSetter()
  // @ts-expect-error #setFormState is called dynamically
  #setFormState(state: string) {
    const [value, count] = state.split('#');
    this.value = value;
    this.count = Number(count);
  }
}

[!WARNING]

TypeScript will complain that a private getter isn't called even if it's decorated and called by the decorator. The FormAssociated API might change so that it doesn't require a @ts-expect-error comment to suppress this warning.

Utilities

  • isDisabled(): An element can be disabled because its ancestor fieldset is disabled, so checking the disabled attribute is insufficient. isDisabled() checks if the element matches :disabled, correctly handling inherited disabled state.

    import {isDisabled} from '@lit-labs/forms';

Contributing

Please see CONTRIBUTING.md.