@opentf/afw-form

A high-performance form engine built on AFW's signals. Fields bind directly to state, updates affect the bindings that read changed fields, 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
pnpm add @opentf/afw-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/afw-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). For radio groups and custom widgets, map their value/events explicitly rather than assuming checkbox behavior.

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.

Last updated on
Edit this page