Wit Form

Comparison

How Wit Form compares with React Hook Form, TanStack Form and Formik: where it fits, and where another library is the better choice.

Each of these libraries makes different trade-offs. This page tries to show them fairly, including the places where Wit Form is behind. The facts were checked against each library's latest release in October 2026: React Hook Form 7.89, TanStack Form 1.33 and Formik 2.4.9. For Wit Form, they describe the current main branch.

At a glance

✅ yes or a strength · ⚠️ partly, or with a catch · ❌ no

Wit FormReact Hook FormTanStack FormFormik
🧠 State modelOne Jotai atom per fieldUncontrolled inputs (refs) + proxy stateControlled, one store with selectorsControlled, one reducer for the form
⚡ Typing re-renders✅ That field and its watchers✅ Nothing for native inputs, or the one Controller✅ That form.Field and its subscribers❌ The whole form (unless FastField)
📦 Size (min + gzip)✅ ~10 kB, plus ~4 kB for Jotai~15 kB~19 kB~15 kB
🔗 Runtime dependenciesJotai (peer)✅ NoneTanStack Store packages⚠️ 8, including lodash
⏳ Async validation✅ Yes, with built-in debounce✅ Yes✅ Yes, with built-in debounce✅ Yes
📐 Schema validation✅ Standard Schema built in (Zod, Valibot, ArkType, Yup 1.7+)✅ Zod, Yup, Valibot and others via @hookform/resolvers✅ Standard Schema built in (Zod, Valibot, ArkType)⚠️ Yup built in
🧪 Validation levelsField, field array, form, each with a schemaField, field array (rules), form, schemaField (including array fields), form, schemaField, form, schema
🔒 Type-safe field names✅ Yes, with createFormHooks✅ Yes✅ Yes❌ No
📋 Field arrays✅ Nested, stable rowIds✅ Nested, stable ids⚠️ Nested, keyed by index⚠️ Nested, keyed by index
🔀 Array operations✅ append, prepend, insert, update, swap, move, remove, clear, replaceappend, prepend, insert, update, swap, move, remove, replaceappend, insert, remove, swap, move, replacepush, insert, remove, swap, move, unshift, pop, replace
📊 Form state flags✅ isValid, isDirty, isSubmitted, submitCount…, re-render only for the flags you read✅ Same set, re-render only for the flags you read✅ Same set, via selectors⚠️ Same set, but every change re-renders the form
🎯 Focus the first invalid field✅ Yes, shouldFocusError✅ Yes, shouldFocusError⚠️ Do it yourself in onSubmitInvalid❌ No
🙈 Value of an unmounted fieldDiscarded (opt out with skipUnregister)Kept (opt in to drop with shouldUnregister)KeptKept
🏷️ Extra data next to a value✅ Yes, extraInfo❌ No❌ No❌ No
👀 Watch one column of an array✅ Yes, useFieldArrayColumnWatch⚠️ Watch the whole array, or each cell by name✅ Subscribe with a selector❌ No, every change re-renders
🖥️ Server actions / SSR helpers❌ No⚠️ <Form> component, no server-action helper✅ Packages for Next.js, TanStack Start, Remix❌ No
🌐 FrameworksReactReact (and React Native)✅ React, Vue, Angular, Solid, Svelte, Lit, PreactReact
🛠️ Devtools❌ No✅ @hookform/devtools⚠️ TanStack Devtools plugin (pre-1.0)❌ No
🔧 Maintenance🌱 New (0.1), successor to react-recoil-form✅ Very active, v8 in beta✅ Active, v2 in alpha⚠️ No commits since November 2025

Sizes are for the full package, minified and gzipped, measured with React external. Jotai costs nothing extra if your app already uses it. Wit Form needs no extra package for schemas; React Hook Form needs @hookform/resolvers for them.

React Hook Form

React Hook Form is the default choice for most React apps, and for good reasons. It's mature, has no dependencies, has typed field names, supports async validation and works with almost every schema library through resolvers. With native inputs and register, typing doesn't re-render React at all, which is cheaper than any of the other libraries here, Wit Form included.

Choose React Hook Form if you want the most widely used option, you mostly use native, uncontrolled inputs, you build for React Native, or you rely on its ecosystem, like devtools or resolvers for libraries that don't implement Standard Schema.

Where Wit Form differs:

  • Every field is controlled, so custom inputs, date pickers and selects don't need a separate Controller path. The same useField works for all of them.
  • Schemas need no extra package. Pass any Standard Schema to a field, a field array or the whole form, and form-level issues land on the matching fields, including fields inside field array rows.
  • Async validators get a built-in debounceValidation, and results of outdated checks are dropped.
  • Fields are dropped from the values when they unmount. In React Hook Form they're kept by default, so a hidden conditional field can still be submitted unless you set shouldUnregister.
  • extraInfo stores data such as an option's label or a File preview next to a value. In React Hook Form you keep that state yourself.
  • useFieldArrayColumnWatch subscribes to only some columns of a field array, which helps with running totals over large tables.

TanStack Form

TanStack Form has the strongest TypeScript story of the four: field names and values are inferred deeply from defaultValues, with no type to write. Async validators come with built-in debouncing, Standard Schema works without an adapter, and it has official packages for Next.js server actions and for other frameworks. Re-renders are scoped to each form.Field, so typing stays as cheap as it is in Wit Form.

Choose TanStack Form if you need server-side validation helpers or server actions, devtools, the same form logic in a framework other than React, or types inferred from defaultValues without declaring a type.

Where Wit Form differs:

  • Less API to learn: plain hooks and plain validator functions, with no validators map. Type safety is opt-in: describe your values once with createFormHooks<Values>() and every hook checks names and values.
  • Field array rows get stable rowIds, so React keys and row state survive inserts, moves and removals without you adding ids to your data. swap and move only reorder rows, so row components don't even re-render.
  • A form schema's issues are mapped onto fields automatically, so one schema can drive the errors of the whole form.
  • Fields are dropped from the values when they unmount. TanStack Form keeps the value and only resets the field's errors.
  • The first invalid field is focused on submit, without extra code.
  • A smaller bundle, especially if you already use Jotai.

Formik

Formik was the most popular form library for years and is still widely installed. Today it's largely unmaintained: the last releases (November 2025) were small React 19 fixes, and the previous one was in April 2024. All state lives in one reducer, so every keystroke re-renders the whole form and every component that reads it. FastField helps, but only for simple cases.

Choose Formik if you're maintaining an app that already uses it and the forms are small. For new projects, any of the other three is a better fit.

Moving from Formik to Wit Form mostly means replacing <Formik> with a FormProvider, useFormik with useForm, and <Field> with useField or Wit Form's <Field>. Formik's validationSchema becomes useForm's schema: Yup 1.7 and newer implements Standard Schema, so the same Yup schema works as is. setFieldError becomes setError, and isValid, dirty, submitCount and isSubmitting are on formState (see Validation).

When Wit Form is a good fit

  • Large, dynamic forms: long tables, nested field arrays, forms with hundreds of inputs. Typing re-renders only the cell you're in, with no memoization on your part. See the Render Performance example.
  • Forms with many custom inputs that need to be controlled anyway.
  • Forms validated by a schema, where one Zod, Valibot or ArkType schema should drive every field's error. See the Schema Rules example.
  • Forms with lots of conditional fields, where hidden fields shouldn't end up in the submitted values.
  • Apps that already use Jotai, where the extra cost is about 10 kB.
  • Apps moving off react-recoil-form: it's a drop-in replacement. See Migrating.

When to pick something else

Be honest with yourself about these. Wit Form doesn't have them today:

  • Server actions, SSR helpers, devtools, or frameworks other than React.
  • Types inferred from your initial values. Wit Form's typed hooks need you to write the values type once. Fields inside field array rows are only checked when TypeScript sees the row names as literal types (inline, or as const).
  • Zero re-renders for native inputs. Every Wit Form field is controlled, so typing re-renders that field's component. React Hook Form's uncontrolled register avoids even that.
  • A large community. Wit Form is new. React Hook Form has far more answers, examples and integrations online.

If any of these is a must-have, React Hook Form or TanStack Form will serve you better.

On this page