Overview
Feature matrix
| Features | Kin Form | React Hook Form | Formik | TanStack Form |
|---|---|---|---|---|
| Zero dependencies | ✅ | ✅ | ❌ | ⚠️ |
| Framework-agnostic core | ✅ | ❌ | ❌ | ✅ |
| Type-safe nested field paths | ✅ | ⚠️ | ❌ | ✅ |
| Standard Schema support | ⚠️ | ⚠️ | ❌ | ✅ |
| Same primitive for field, group, array, and form | ✅ | ❌ | ❌ | ❌ |
| Localized subscription | ✅ | ❌ | ❌ | ❌ |
| Selective re-rendering | ✅ | ⚠️ | ⚠️ | ✅ |
| Built-in async-validation debounce | ✅ | ❌ | ❌ | ✅ |
| Declarative cross-field revalidation | ✅ | ⚠️ | ❌ | ✅ |
| Field state survives list virtualization | ✅ | ⚠️ | ✅ | ✅ |
✅ full support · ⚠️ partial or conditional · ❌ not supported
API surface
Not whether a feature exists, but which APIs/hooks/types you have to learn to use it:
| Kin Form | React Hook Form | Formik | TanStack Form | |
|---|---|---|---|---|
| Types | FieldApiFormApi | FieldPathFieldPathByValueFieldArrayPathUseControllerProps | — | FieldApiFieldGroupApiFormApi |
| Hooks & components | useFormuseWatchWatchuseMultistep | useFormregisteruseControllerControlleruseFieldArrayuseWatchuseFormState | useFormikFormikuseFieldFieldFieldArrayuseFormikContext | useFormuseFieldFielduseFieldGroupuseFormGroupFormGroupuseStoreSubscribecreateFormHookuseAppFormAppFieldAppFormwithFormwithFieldGroupuseFormId |
Bundle size (React usage)
Every library above is measured directly with the same toolchain: rolldown and
its own built-in minifier, then gzip. That's not the same as Bundlephobia, which
minifies with terser by default and can produce different sizes for the same
source. Reproducible via deno task --cwd scripts bundle-size.
Performance
Smaller is better.
Initial mount (84 fields)
Flat field update burst (800x)
Nested field update burst (800x)
Array swap burst (800x, cycling adjacent pairs)
Array insert/remove burst (800x)
Flat field update burst + sync validator (800x)
Debounced async validation (800 updates, 50ms debounce)
Whole-form zod schema validation (on submit)
Untouched sibling re-renders (800 updates)
Flat field update burst (800x)
Nested field update burst (800x)
These are wall-clock numbers from one shared ~84-field form (flat fields, a
nested group, an array) in a real (Happy DOM) React tree, reproducible via
deno task --cwd scripts speed-bench.
The benchmark also counts re-renders per scenario, how many untouched sibling fields re-render when one field updates, charted above for the two scenarios where that count is clean and isolated.