Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
9edad01
feat: initial work on FormGroupApi in core
crutchcorn Apr 16, 2026
dbc7ff6
chore: initial (and wrong) per-form-group-validation
crutchcorn Apr 16, 2026
1a22f52
chore: create a common FieldLikeAPI to adopt in form groups shortly
crutchcorn Apr 16, 2026
4775996
chore: implement FieldLike API on FormGroup
crutchcorn Apr 16, 2026
756ddf8
chore: revert changes to FormApi validation logic
crutchcorn Apr 16, 2026
c553439
chore: fix type issues with validation kind
crutchcorn Apr 16, 2026
d2eaa29
chore: minor fixes
crutchcorn Apr 16, 2026
ac22e7b
Revert "chore: revert changes to FormApi validation logic"
crutchcorn Apr 16, 2026
c2d061b
chore: filter fields to validate in formgroup
crutchcorn Apr 16, 2026
433ad6e
chore: improve store
crutchcorn Apr 16, 2026
22e49a7
chore: fix tests
crutchcorn Apr 16, 2026
db88246
chore: fix another test
crutchcorn Apr 16, 2026
4eb9b0f
chore: add submitmeta test
crutchcorn Apr 16, 2026
61d36cd
ci: apply automated fixes and generate docs
autofix-ci[bot] Apr 16, 2026
9b8bcb5
chore: add FormLike methods to FormGroup
crutchcorn Apr 20, 2026
86d9000
Merge branch 'form-group' of https://github.com/TanStack/form into fo…
crutchcorn Apr 20, 2026
d990bc6
chore: add type tests
crutchcorn Apr 22, 2026
66c5db9
chore: fix build
crutchcorn Apr 22, 2026
3c8ff5c
chore: fix type tests
crutchcorn Apr 22, 2026
4e4b272
ci: apply automated fixes and generate docs
autofix-ci[bot] Apr 22, 2026
ce63f8f
Merge branch 'main' into form-group
crutchcorn May 6, 2026
6094b9c
chore: export two missing items
crutchcorn May 6, 2026
1f96984
chore: add new interop FormGroupOptions type
crutchcorn May 6, 2026
ee121e4
chore: initial implement of useFormGroup in React
crutchcorn May 6, 2026
c46e342
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
3b756ae
chore: add formGroup to useForm
crutchcorn May 6, 2026
93ec42f
chore: add initial React implementation tests
crutchcorn May 6, 2026
da467f3
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
d1471f6
chore: add type tests for React adapter
crutchcorn May 6, 2026
e3e81b9
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
23e8066
chore: fix eslint
crutchcorn May 6, 2026
c86b424
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
4b81818
chore: add multi-step wizard
crutchcorn May 6, 2026
40b2758
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
e9ee38c
fix: handle resubmissions
crutchcorn May 6, 2026
bfa9464
fix: validate on resubmit
crutchcorn May 6, 2026
a86f781
feat: add better onDynamic handling to FormGroupApi
crutchcorn May 6, 2026
fec4e73
chore: first attempt to fix onDynamic change handling on groups
crutchcorn May 6, 2026
c3d6176
chore: attempt 2
crutchcorn May 6, 2026
973fab7
chore: attempt 3
crutchcorn May 6, 2026
7546f08
chore: attempt 4
crutchcorn May 6, 2026
c8ec12e
Revert "chore: attempt 4"
crutchcorn May 6, 2026
d499d20
chore: finalize fixing form errors
crutchcorn May 6, 2026
58ed8a2
chore: fix eslint
crutchcorn May 6, 2026
f69771b
chore: regenerate lockfile
crutchcorn May 6, 2026
7d9ca0d
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
9e78281
docs: update the wizard example
crutchcorn May 6, 2026
4837dd9
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
b189273
chore: initial work on Vue FormGroup API
crutchcorn May 7, 2026
1ee623c
ci: apply automated fixes and generate docs
autofix-ci[bot] May 7, 2026
1061988
Merge branch 'main' into form-group
crutchcorn May 8, 2026
e333776
chore: revert useContext changes
crutchcorn May 8, 2026
ad5bdd7
Merge branch 'main' into form-group
crutchcorn May 8, 2026
9371cfb
feat: add preact form group
crutchcorn May 8, 2026
d9f8097
chore: fix tests and export names
crutchcorn May 8, 2026
fa7ed5e
chore: add Preact wizard demo
crutchcorn May 8, 2026
0fbc378
feat: add FormGroup support for Solid adapter
crutchcorn May 8, 2026
caf608e
chore: add formgroup tests for Solid
crutchcorn May 8, 2026
30f20f2
chore: add type tests for Solid
crutchcorn May 8, 2026
d14c6c7
ci: apply automated fixes and generate docs
autofix-ci[bot] May 8, 2026
5458c9e
chore: add Solid group example
crutchcorn May 8, 2026
dcaebbe
ci: apply automated fixes and generate docs
autofix-ci[bot] May 8, 2026
a9c2e92
chore: fix test
crutchcorn May 8, 2026
71fb870
Merge branch 'main' into form-group
crutchcorn May 9, 2026
c5c3798
Merge branch 'main' into form-group
crutchcorn May 9, 2026
5c6a7e4
feat: add FormGroup impl to Svelte
crutchcorn May 9, 2026
ab4ab8b
ci: apply automated fixes and generate docs
autofix-ci[bot] May 9, 2026
85a036a
chore: add formGroup tests for Svelte
crutchcorn May 9, 2026
ec039ad
ci: apply automated fixes and generate docs
autofix-ci[bot] May 9, 2026
9e085a8
chore: add Svelte example
crutchcorn May 9, 2026
5bf025e
chore: fix sherif
crutchcorn May 9, 2026
3f386d9
feat: add Lit FormGroup API
crutchcorn May 9, 2026
2ff22da
chore: add Lit FormGroup tests
crutchcorn May 9, 2026
d711535
chore: refactor Lit tests
crutchcorn May 9, 2026
a8d754f
ci: apply automated fixes and generate docs
autofix-ci[bot] May 9, 2026
75b8306
feat: implement FormGroup in Angular
crutchcorn May 9, 2026
47a8664
chore: add FormGroup Angular spec
crutchcorn May 9, 2026
3a18322
ci: apply automated fixes and generate docs
autofix-ci[bot] May 9, 2026
53412be
feat: add withForm feature to Angular
crutchcorn May 9, 2026
02ca57e
chore: update Angular large-form example to use new `withForm` API
crutchcorn May 9, 2026
8cc45ec
docs: add docs to Angular withForm example
crutchcorn May 9, 2026
3a6f632
chore: add changeset
crutchcorn May 9, 2026
6edf9d4
chore: fix knip
crutchcorn May 9, 2026
596c8d5
chore: initial multi-step-wizard Angular example
crutchcorn May 9, 2026
001068c
Merge branch 'angular-with-form' into form-group
crutchcorn May 9, 2026
31c616a
chore: update the multi-step-wizard to use new API
crutchcorn May 9, 2026
4bc606c
Merge branch 'main' into form-group
crutchcorn May 9, 2026
771ff94
Merge branch 'main' into form-group
crutchcorn May 10, 2026
36f775c
Merge branch 'main' into form-group
crutchcorn May 10, 2026
db61223
chore: add multi-step-wizard example to Lit
crutchcorn May 10, 2026
9f68cbd
ci: apply automated fixes and generate docs
autofix-ci[bot] May 10, 2026
87d61f5
chore: add Vue example
crutchcorn May 10, 2026
c05d2ac
chore: fix CI
crutchcorn May 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/form-core/src/FieldApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ export interface FieldListeners<
onMount?: FieldListenerFn<TParentData, TName, TData>
onUnmount?: FieldListenerFn<TParentData, TName, TData>
onSubmit?: FieldListenerFn<TParentData, TName, TData>
onGroupSubmit?: FieldListenerFn<TParentData, TName, TData>
}

/**
Expand Down
75 changes: 53 additions & 22 deletions packages/form-core/src/FormApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,13 @@ export type AnyFormApi = FormApi<
any
>

interface ValidateOpts<TFormData> {
// Useful in FormGroup where validation doesn't update form error map
dontUpdateFormErrorMap?: boolean
// Filter which field names to validate, useful for FormGroup validation to filter out fields that don't start with the FormGroup name
filterFieldNames?: (fieldName: DeepKeys<TFormData>) => boolean
}

/**
* We cannot use methods and must use arrow functions. Otherwise, our React adapters
* will break due to loss of the method when using spread.
Expand Down Expand Up @@ -1654,6 +1661,7 @@ export class FormApi<
*/
validateSync = (
cause: ValidationCause,
validateOpts?: ValidateOpts<TFormData>,
): {
hasErrored: boolean
fieldsErrorMap: FormErrorMapFromValidator<
Expand Down Expand Up @@ -1709,11 +1717,17 @@ export class FormApi<

const errorMapKey = getErrorMapKey(validateObj.cause)

const allFieldsToProcess = new Set([
let allFieldsToProcess = new Set([
...Object.keys(this.state.fieldMeta),
...Object.keys(fieldErrors || {}),
] as DeepKeys<TFormData>[])

if (validateOpts?.filterFieldNames) {
allFieldsToProcess = new Set(
[...allFieldsToProcess].filter(validateOpts.filterFieldNames),
)
}

for (const field of allFieldsToProcess) {
if (
this.baseStore.state.fieldMetaBase[field] === undefined &&
Expand Down Expand Up @@ -1765,22 +1779,28 @@ export class FormApi<
}
}

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (this.state.errorMap?.[errorMapKey] !== formError) {
this.baseStore.setState((prev) => ({
...prev,
errorMap: {
...prev.errorMap,
[errorMapKey]: formError,
},
}))
if (!validateOpts?.dontUpdateFormErrorMap) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (this.state.errorMap?.[errorMapKey] !== formError) {
this.baseStore.setState((prev) => ({
...prev,
errorMap: {
...prev.errorMap,
[errorMapKey]: formError,
},
}))
}
}

if (formError || fieldErrors) {
hasErrored = true
}
}

if (validateOpts?.dontUpdateFormErrorMap) {
return
}

/**
* when we have an error for onSubmit in the state, we want
* to clear the error as soon as the user enters a valid value in the field
Expand Down Expand Up @@ -1830,6 +1850,7 @@ export class FormApi<
*/
validateAsync = async (
cause: ValidationCause,
validateOpts?: ValidateOpts<TFormData>,
): Promise<
FormErrorMapFromValidator<
TFormData,
Expand Down Expand Up @@ -1920,9 +1941,13 @@ export class FormApi<
}
const errorMapKey = getErrorMapKey(validateObj.cause)

for (const field of Object.keys(
this.state.fieldMeta,
) as DeepKeys<TFormData>[]) {
let fields: DeepKeys<TFormData>[] = Object.keys(this.state.fieldMeta)

if (validateOpts?.filterFieldNames) {
fields = fields.filter(validateOpts.filterFieldNames)
}

for (const field of fields) {
if (this.baseStore.state.fieldMetaBase[field] === undefined) {
continue
}
Expand Down Expand Up @@ -1965,13 +1990,15 @@ export class FormApi<
}
}

this.baseStore.setState((prev) => ({
...prev,
errorMap: {
...prev.errorMap,
[errorMapKey]: formError,
},
}))
if (!validateOpts?.dontUpdateFormErrorMap) {
this.baseStore.setState((prev) => ({
...prev,
errorMap: {
...prev.errorMap,
[errorMapKey]: formError,
},
}))
}

resolve(
fieldErrorsFromFormValidators
Expand Down Expand Up @@ -2030,6 +2057,7 @@ export class FormApi<
*/
validate = (
cause: ValidationCause,
validateOpts?: ValidateOpts<TFormData>,
):
| FormErrorMapFromValidator<
TFormData,
Expand Down Expand Up @@ -2058,14 +2086,17 @@ export class FormApi<
>
> => {
// Attempt to sync validate first
const { hasErrored, fieldsErrorMap } = this.validateSync(cause)
const { hasErrored, fieldsErrorMap } = this.validateSync(
cause,
validateOpts,
)

if (hasErrored && !this.options.asyncAlways) {
return fieldsErrorMap
}

// No error? Attempt async validation
return this.validateAsync(cause)
return this.validateAsync(cause, validateOpts)
}

// Needs to edgecase in the React adapter specifically to avoid type errors
Expand Down
Loading
Loading