| Basic form | useForm({ defaultValues, onSubmit }) | Form instance with Field component |
| Field | form.Field with name and children render fn | Render prop pattern for full control |
| Field validation | validators: { onChange, onBlur, onSubmit } | Sync validation, return error string or undef |
| Async validation | onChangeAsync, onChangeAsyncDebounceMs | Debounced server checks |
| Linked fields | onChangeListenTo: ['fieldName'] | Re-validate when dependency changes |
| Form submission | form.handleSubmit() | Validates and calls onSubmit if valid |
| Form state | form.state.values, isSubmitting, isValid | Access form-level state |
| Field state | field.state.value, meta.errors, meta.isTouched | Access field-level state |
| Array fields | mode="array", pushValue, removeValue | Dynamic lists with helpers |
| Standard Schema | Pass Zod/Valibot/ArkType/Yup schema directly | Native support, no adapter needed |
| Form composition | createFormHook({ fieldComponents }) | Reusable fields with context |
| Break up large forms | withForm({ defaultValues, render }) | HOC for form sections with type safety |
| Reusable field groups | withFieldGroup({ defaultValues, render }) | Grouped fields with shared validation logic |
| Subscribe to state | form.Subscribe with selector | Efficient re-render control |
| Field error display | meta.isTouched && meta.errors.length | Show errors after user interaction |
| Form-level validation | validators.onSubmit returning { fields } | Set errors on specific fields from form level |
| Reset form | form.reset() | Reset to defaultValues |