Getting Started
What is Kin Form?
Kin Form is a library that makes working with forms easy in Lit applications.
It is packed with almost everything you need to create forms in a flexible, declarative, and type-safe way.
The name Kin Form is derived from my nickname (Kin), for the sake of simplicity. We all know how hard it is to name things!
Motivation
Kin Form started many years ago when I needed to build a complex admin portal using Lit for my company Accumulus. There were roughly 40 forms, ranging from simple ones with a few fields to complex ones with deeply nested objects and arrays. I couldn't find a robust form management library for Lit, so I decided to build my own.
After its success at Accumulus, I continued to refine the library and used it to build another admin portal for Restful Mind.
The library has served me so well that I believe it will benefit many others too. That's why I decided to turn it into an open source project.
Installation
Kin Form is available on jsr.
deno add jsr:@kin/form
pnpm add jsr:@kin/form # v10.9.0+
yarn add jsr:@kin/form # v4.9+
npx jsr add @kin/form
Basic Usage
Here is a complete example of a simple login form to get you started.
First, ensure you have the necessary imports and that you have defined custom elements for your form fields (e.g., text-field
). For simplicity, this example assumes you have a text-field
component that extends FormField
.
import { LitElement, html } from "lit";
import { customElement } from "lit/decorators.js";
import { FormController } from "@kin/form";
// Assumes the existence of a <text-field> component that extends KinField.
import "./text-field.ts";
interface User {
email: string;
name: string;
}
@customElement("login-form")
export class LoginForm extends LitElement {
#form = new FormController<User>(this, {
initialValue: {
email: "",
name: "",
},
onSubmit: async (form) => {
// Simulate an API call.
await new Promise((resolve) => setTimeout(resolve, 1000));
alert(`Submitted: ${JSON.stringify(form.value, null, 2)}`);
},
});
protected override render() {
const { field, handleSubmit, submitting } = this.#form;
return html`
<text-field label="Email" ${field("email")}></text-field>
<text-field label="Name" ${field("name")}></text-field>
<button .disabled=${submitting} @submit=${handleSubmit}>
${submitting ? "Logging in..." : "Log in"}
</button>
`;
}
}
Key Concepts
-
FormController
: This is the heart of your form. You create an instance of it in your Lit component, providing it with an initial data shape (initialValue
) and a submission handler (onSubmit
). -
field()
directive: This directive binds a form field component (like<text-field>
) to a specific property in your form's data model (e.g.,email
). -
handleSubmit()
: This function, provided by the controller, orchestrates the submission process. It calls youronSubmit
callback if the form is valid.