@opentf/web-form

A high-performance form engine built on OTF Web's signals. Fields bind directly to state, updates are surgical (no re-render), and deeply nested values are addressed by path string.

A full multi-step booking flow on one shared form — tap through it and watch the live state on the right:

Flight Booking FormDemo
1
2
3
4
5
1

Installation

Install the package from npm with your preferred package manager:

Shell
bun add @opentf/web-form

createForm

createForm returns a stable form object holding reactive values, errors, and status flags. Bind inputs with register and submit with handleSubmit.

JSX
import { createForm } from "@opentf/web-form";

export function LoginForm() {
  const form = createForm({
    initialValues: { email: "", password: "" },
  });

  const onSubmit = (values) => console.log(values);

  return (
    <form onsubmit={form.handleSubmit(onSubmit)}>
      <input {...form.register("email")} type="email" />
      <input {...form.register("password")} type="password" />
      <button type="submit">Sign in</button>
    </form>
  );
}

The same form, running live:

register(path) spreads everything a field needs — name, value, checked, error, isTouched, oninput, and onblur — so a controlled input is one line.

Works with any input

register follows the standard value / oninput contract, so it binds native inputs and custom components alike — including checkboxes (it also returns checked).

Reactive state

Read form.values, form.errors, and form.touched directly in your template — they are reactive proxies, so only the nodes that read a changed path update.

JSX
<p>Hello, {form.values.email || "stranger"}</p>
{form.errors.email ? <span class="error">{form.errors.email}</span> : null}
Live demo
form.values.name
Hello, stranger 👋

Status flags

The form exposes derived flags for submit buttons and UI state:

FlagMeaning
isValidNo errors are currently set.
isChangedValues differ from initialValues.
isTouchedAt least one field has been blurred.
isSubmittingAn async submit handler is in flight.
isValidatingAn async validator is running.
isSubmittedA submit has completed successfully.
submitCountNumber of submit attempts.
JSX
<button type="submit" disabled={form.isSubmitting || !form.isValid}>
  {form.isSubmitting ? "Saving…" : "Save"}
</button>
Live demo
isValidisChangedisTouchedisSubmittingisSubmittedsubmitCount: 0

Next: Validation · Nested State · API Reference.