Form Groups#2128
Conversation
📝 WalkthroughWalkthroughThis pull request adds FormGroup support: a new FormGroupApi class, a FieldListeners onGroupSubmit hook, extended FormApi validate signatures with ValidateOpts to filter fields and optionally skip updating the form error map, plus tests, docs, and multi-framework example apps demonstrating group submission and validation. ChangesForm Groups Feature
Sequence DiagramsequenceDiagram
participant Caller
participant GroupApi as FormGroupApi
participant FieldApis as FieldApi Instances
participant FormApi
Caller->>GroupApi: handleSubmit()
GroupApi->>GroupApi: reset flags, increment submissionAttempts, mark related fields touched, set isSubmitting = true
GroupApi->>FieldApis: validate each field for 'submit'
FieldApis-->>GroupApi: field validity
alt any group field invalid
GroupApi->>GroupApi: set isSubmitting = false
GroupApi->>GroupApi: invoke onGroupSubmitInvalid
GroupApi-->>Caller: early return
else group fields valid
GroupApi->>FormApi: validate('submit', { dontUpdateFormErrorMap, filterFieldNames })
FormApi-->>GroupApi: form validation result
alt form invalid for those fields
GroupApi->>GroupApi: set isSubmitting = false
GroupApi->>GroupApi: invoke onGroupSubmitInvalid
GroupApi-->>Caller: early return
else all valid
GroupApi->>FieldApis: trigger onGroupSubmit listeners on fields
GroupApi->>GroupApi: invoke options.listeners?.onSubmit and options.onGroupSubmit()
alt submission succeeds
GroupApi->>GroupApi: set isSubmitted = true, isSubmitSuccessful = true
else submission fails
GroupApi->>GroupApi: set isSubmitSuccessful = false
GroupApi->>GroupApi: set isSubmitting = false
GroupApi-->>Caller: rethrow error
end
GroupApi->>GroupApi: set isSubmitting = false
GroupApi-->>Caller: resolve
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit f8c2a72
☁️ Nx Cloud last updated this comment at |
🚀 Changeset Version PreviewNo changeset entries found. Merging this PR will not cause a version bump for any packages. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2128 +/- ##
==========================================
- Coverage 90.35% 88.19% -2.16%
==========================================
Files 38 65 +27
Lines 1752 3109 +1357
Branches 444 765 +321
==========================================
+ Hits 1583 2742 +1159
- Misses 149 328 +179
- Partials 20 39 +19 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (3)
packages/form-core/tests/FormGroupApi.spec.ts (1)
73-76: Consider adding assertion for field error state.The test verifies callback invocations but doesn't assert that the field actually has errors after validation. Adding such an assertion would strengthen the test:
expect(step1NameField.state.meta.errors.length).toBeGreaterThan(0)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/form-core/tests/FormGroupApi.spec.ts` around lines 73 - 76, Add an assertion to verify the field's error state after validation: after invoking validation callbacks in the test, assert that step1NameField.state.meta.errors contains at least one error (e.g., use an assertion like checking length > 0) so the test not only checks callback invocation but also that FieldApi (step1NameField) actually recorded validation errors.packages/form-core/src/FormGroupApi.ts (2)
124-126: Emptymount()method may need cleanup logic later.The mount method currently does nothing and returns an empty cleanup function. As the implementation matures, consider whether mount should:
- Subscribe to form state changes
- Initialize group-specific state
- Register the group with the form for coordinated lifecycle management
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/form-core/src/FormGroupApi.ts` around lines 124 - 126, The mount() method on FormGroupApi is currently a no-op; replace it with initialization and teardown logic: when mounting the group, perform any group-specific state initialization, subscribe to the parent form's change events (or call the form's registration API such as registerGroup or similar), and return a cleanup function that unsubscribes and deregisters the group (e.g., call unregisterGroup/removeGroup) to avoid leaks; ensure you reference the FormGroupApi instance state and the form-level APIs used to subscribe/register so the returned function reverses those actions.
216-220: Placeholder values (value: 0) need to be replaced with actual group values.Multiple callback invocations use
value: 0as a placeholder. This is noted in comments but tracking for completeness. Consider using a more explicit placeholder likeundefinedor extracting the actual group value subset.Also applies to: 234-238, 275-278, 281-285
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/form-core/src/FormGroupApi.ts` around lines 216 - 220, The callback invocations currently pass a placeholder value (value: 0) instead of the actual group values; replace those placeholders in the onGroup* calls (e.g., this.options.onGroupSubmitInvalid, the similar calls at the ranges 234-238, 275-278, 281-285) with the real subset of form state for this group by extracting the group's values from this.state.values (for example build a groupValues object by selecting the group's field keys from this.state.values) and pass that object as value, or if no values exist pass undefined explicitly; ensure you reference the group field list used by this class when building the subset so the callbacks receive accurate group data.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/form-core/src/FormGroupApi.ts`:
- Around line 274-285: Remove the duplicate invocation of
this.options.onGroupSubmit in FormGroupApi: delete the redundant first call and
keep a single call that passes the real submit payload (use this.state.values
instead of 0 and submitMetaArg instead of {} if those are the intended
variables). Ensure only one this.options.onGroupSubmit is called and it uses the
correct arguments (this.state.values, this.options.form, submitMetaArg).
- Around line 128-131: The startsWith check in _isFieldNamePartOfGroup
incorrectly matches names like "step10" for group "step1"; update
_isFieldNamePartOfGroup (and use this.options.name) to verify a proper boundary
after the prefix: return true only if fieldName === prefix or fieldName
startsWith(prefix + '.') or fieldName startsWith(prefix + '[') (i.e., check that
the character immediately after the prefix is absent or is a separator like '.'
or '[') so unrelated names aren't included.
- Around line 225-241: The code validates group-scoped fields via
this.options.form.validate(..., filterFieldNames: this._isFieldNamePartOfGroup)
but then checks the entire form state with this.options.form.state.isValid;
replace that check with a call to this._isFieldsValid() so the submission
decision uses the same group-scoped validation logic (keeping the done() call
and the this.options.onGroupSubmitInvalid invocation intact), i.e., use
_isFieldsValid() instead of reading form.state.isValid after the filtered
validate call.
In `@packages/form-core/tests/FormGroupApi.spec.ts`:
- Around line 49-60: The validator in the test's onSubmit returns a nested
fields object (step1: { name: { required: true } }) but
GlobalFormValidationError expects flat field paths mapping to ValidationError;
update the onSubmit return to use flat keys like 'step1.name': { required: true
} (i.e., fields: { 'step1.name': { required: true } }) so
Object.keys(fieldErrors) yields the full field path and each value is a proper
ValidationError per the GlobalFormValidationError /
Partial<Record<DeepKeys<TFormData>, ValidationError>> shape.
---
Nitpick comments:
In `@packages/form-core/src/FormGroupApi.ts`:
- Around line 124-126: The mount() method on FormGroupApi is currently a no-op;
replace it with initialization and teardown logic: when mounting the group,
perform any group-specific state initialization, subscribe to the parent form's
change events (or call the form's registration API such as registerGroup or
similar), and return a cleanup function that unsubscribes and deregisters the
group (e.g., call unregisterGroup/removeGroup) to avoid leaks; ensure you
reference the FormGroupApi instance state and the form-level APIs used to
subscribe/register so the returned function reverses those actions.
- Around line 216-220: The callback invocations currently pass a placeholder
value (value: 0) instead of the actual group values; replace those placeholders
in the onGroup* calls (e.g., this.options.onGroupSubmitInvalid, the similar
calls at the ranges 234-238, 275-278, 281-285) with the real subset of form
state for this group by extracting the group's values from this.state.values
(for example build a groupValues object by selecting the group's field keys from
this.state.values) and pass that object as value, or if no values exist pass
undefined explicitly; ensure you reference the group field list used by this
class when building the subset so the callbacks receive accurate group data.
In `@packages/form-core/tests/FormGroupApi.spec.ts`:
- Around line 73-76: Add an assertion to verify the field's error state after
validation: after invoking validation callbacks in the test, assert that
step1NameField.state.meta.errors contains at least one error (e.g., use an
assertion like checking length > 0) so the test not only checks callback
invocation but also that FieldApi (step1NameField) actually recorded validation
errors.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ac7e0310-fbaa-4485-b402-aece0b9fb522
📒 Files selected for processing (5)
packages/form-core/src/FieldApi.tspackages/form-core/src/FormApi.tspackages/form-core/src/FormGroupApi.tspackages/form-core/src/index.tspackages/form-core/tests/FormGroupApi.spec.ts
| _isFieldNamePartOfGroup = (fieldName: string) => { | ||
| // TODO: Does this `startWith` capture sub-field names properly? Probably not. :( | ||
| return fieldName.startsWith(this.options.name) | ||
| } |
There was a problem hiding this comment.
startsWith check can incorrectly match unrelated fields.
If the group name is "step1", then "step10.name".startsWith("step1") returns true, incorrectly including step10 fields in the group. Add a boundary check:
_isFieldNamePartOfGroup = (fieldName: string) => {
- // TODO: Does this `startWith` capture sub-field names properly? Probably not. :(
- return fieldName.startsWith(this.options.name)
+ const name = this.options.name
+ return (
+ fieldName === name ||
+ fieldName.startsWith(`${name}.`) ||
+ fieldName.startsWith(`${name}[`)
+ )
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| _isFieldNamePartOfGroup = (fieldName: string) => { | |
| // TODO: Does this `startWith` capture sub-field names properly? Probably not. :( | |
| return fieldName.startsWith(this.options.name) | |
| } | |
| _isFieldNamePartOfGroup = (fieldName: string) => { | |
| const name = this.options.name | |
| return ( | |
| fieldName === name || | |
| fieldName.startsWith(`${name}.`) || | |
| fieldName.startsWith(`${name}[`) | |
| ) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/form-core/src/FormGroupApi.ts` around lines 128 - 131, The
startsWith check in _isFieldNamePartOfGroup incorrectly matches names like
"step10" for group "step1"; update _isFieldNamePartOfGroup (and use
this.options.name) to verify a proper boundary after the prefix: return true
only if fieldName === prefix or fieldName startsWith(prefix + '.') or fieldName
startsWith(prefix + '[') (i.e., check that the character immediately after the
prefix is absent or is a separator like '.' or '[') so unrelated names aren't
included.
| onSubmit: () => { | ||
| return { | ||
| fields: { | ||
| step1: { | ||
| name: { | ||
| required: true, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| }, | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify how field errors are structured in existing tests
rg -n "fields:" packages/form-core/tests/ -A 5 --type=ts | head -60Repository: TanStack/form
Length of output: 4099
🏁 Script executed:
# Find and examine the normalizeError function in FormApi.ts
rg -n "normalizeError" packages/form-core/src/ -A 10 --type=tsRepository: TanStack/form
Length of output: 6024
🏁 Script executed:
# Check the full test context to understand what assertions are made
sed -n '39,87p' packages/form-core/tests/FormGroupApi.spec.tsRepository: TanStack/form
Length of output: 1118
🏁 Script executed:
# Check if nested object error format is used elsewhere in tests
rg -n "required: true" packages/form-core/tests/ --type=tsRepository: TanStack/form
Length of output: 307
🏁 Script executed:
# Check how fieldErrors are processed and matched to fields
rg -n "fieldErrors" packages/form-core/src/FormApi.ts -B 2 -A 3 | head -80Repository: TanStack/form
Length of output: 2834
🏁 Script executed:
# Look at FormGroupApi to see how it handles field errors
cat packages/form-core/src/FormGroupApi.ts | head -100Repository: TanStack/form
Length of output: 2550
🏁 Script executed:
# Check field error assignment logic
rg -n "setFieldMeta\|errorMap" packages/form-core/src/FormApi.ts -B 1 -A 2 | head -50Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Get the full test and see what field is being checked
sed -n '39,90p' packages/form-core/tests/FormGroupApi.spec.tsRepository: TanStack/form
Length of output: 1269
🏁 Script executed:
# Check if step1NameField.state.errors is asserted
rg -n "step1NameField" packages/form-core/tests/FormGroupApi.spec.ts -A 2 -B 1 | head -30Repository: TanStack/form
Length of output: 655
🏁 Script executed:
# Check if there are any assertions on step1NameField.state.meta or errors
rg -n "step1NameField\." packages/form-core/tests/FormGroupApi.spec.ts | head -20Repository: TanStack/form
Length of output: 147
🏁 Script executed:
# Check the type definition of ValidationError to understand what structure is expected
rg -n "type ValidationError" packages/form-core/src/ -A 3Repository: TanStack/form
Length of output: 1049
🏁 Script executed:
# Check how the error is actually processed - look for any recursive processing of nested errors
rg -n "determineFormLevelErrorSourceAndValue" packages/form-core/src/FormApi.ts -A 20 | head -60Repository: TanStack/form
Length of output: 2709
🏁 Script executed:
# Also check if there's any flattening of nested error structures
rg -n "flatten\|flat\|nested" packages/form-core/src/FormApi.ts | head -20Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# Check the GlobalFormValidationError type definition
rg -n "GlobalFormValidationError\|isGlobalFormValidationError" packages/form-core/src/ -B 2 -A 5 | head -80Repository: TanStack/form
Length of output: 39
🏁 Script executed:
# See real usage examples in other validators
rg -n "return {" packages/form-core/tests/ -A 8 --type=ts | grep -A 8 "fields:" | head -50Repository: TanStack/form
Length of output: 136
🏁 Script executed:
# Check the GlobalFormValidationError type definition
rg -n "GlobalFormValidationError" packages/form-core/src/utils.ts -B 2 -A 8Repository: TanStack/form
Length of output: 732
🏁 Script executed:
# Search for validator return examples more carefully
rg -n "fields:" packages/form-core/tests/FormGroupApi.spec.ts -B 2 -A 4Repository: TanStack/form
Length of output: 441
🏁 Script executed:
# Check the GlobalFormValidationError type definition in types.ts
rg -n "GlobalFormValidationError" packages/form-core/src/types.ts -B 2 -A 8Repository: TanStack/form
Length of output: 1352
🏁 Script executed:
# Check what the determineFormLevelErrorSourceAndValue function does
rg -n "export.*determineFormLevelErrorSourceAndValue" packages/form-core/src/utils.ts -A 30Repository: TanStack/form
Length of output: 1166
🏁 Script executed:
# Let me verify one more thing - check if field name iteration includes both nested and flat keys
sed -n '1720,1750p' packages/form-core/src/FormApi.tsRepository: TanStack/form
Length of output: 1083
🏁 Script executed:
# Also check test file to see if there are assertions on field.state properties anywhere
rg -n "expect.*step1NameField" packages/form-core/tests/FormGroupApi.spec.tsRepository: TanStack/form
Length of output: 39
Fix the validator error format to use flat field paths with proper ValidationError values.
The validator returns { fields: { step1: { name: { required: true } } } }, but GlobalFormValidationError expects field keys to be flat paths matching field names:
fields: Partial<Record<DeepKeys<TFormData>, ValidationError>>With the current nested structure, the error for step1.name is not properly mapped because:
Object.keys(fieldErrors)yields['step1']instead of['step1.name']- When checking
fieldErrors['step1.name'], it returnsundefined(error is lost) - The field receives the nested object
{ name: { required: true } }instead of a properValidationError
Correct the validator to return:
validators: {
onSubmit: () => {
return {
fields: {
- step1: {
- name: {
- required: true,
- },
- },
+ 'step1.name': 'Name is required',
},
}
},
},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/form-core/tests/FormGroupApi.spec.ts` around lines 49 - 60, The
validator in the test's onSubmit returns a nested fields object (step1: { name:
{ required: true } }) but GlobalFormValidationError expects flat field paths
mapping to ValidationError; update the onSubmit return to use flat keys like
'step1.name': { required: true } (i.e., fields: { 'step1.name': { required: true
} }) so Object.keys(fieldErrors) yields the full field path and each value is a
proper ValidationError per the GlobalFormValidationError /
Partial<Record<DeepKeys<TFormData>, ValidationError>> shape.
This is wrong because instead of keeping the state inside of `FormGroupApi` itself, we need to follow the same pattern of `FieldApi` and keep the state in `FormApi` and reference that state in a derived way.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This reverts commit 756ddf8.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2128 +/- ##
==========================================
- Coverage 90.35% 88.31% -2.05%
==========================================
Files 38 65 +27
Lines 1752 3140 +1388
Branches 444 781 +337
==========================================
+ Hits 1583 2773 +1190
- Misses 149 328 +179
- Partials 20 39 +19 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. |
# Conflicts: # examples/react/next-server-actions-zod/package.json # examples/react/next-server-actions/package.json # examples/react/remix/package.json # pnpm-lock.yaml
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (2)
docs/framework/preact/guides/form-groups.md (1)
134-143: ⚡ Quick winAdd language identifier to the code fence.
The code block starting at line 134 should specify a language (e.g.,
typescriptorts) for proper syntax highlighting.📝 Proposed fix
-> ``` +> ```typescript > const step1Schema = z.object({ > name: z.string().min(2) > })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/framework/preact/guides/form-groups.md` around lines 134 - 143, The fenced code block containing the schema definitions (const step1Schema, step2Schema, and const schema using z.object) should include a language identifier for syntax highlighting; update the opening fence from ``` to ```typescript (or ```ts) so the block with step1Schema and schema is marked as TypeScript.examples/lit/multi-step-wizard/package.json (1)
15-15: ⚡ Quick winRemove the “invalid Zod version” / installation-failure concern for
^3.25.76.
zod@3.25.76is a published release on npm/GitHub, so^3.25.76should resolve successfully to a valid Zod 3.x version. If the intent is to use the newest Zod, consider upgrading to Zod 4.x (the npm default “latest” major is now 4.x).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/lit/multi-step-wizard/package.json` at line 15, The review flags an incorrect "invalid Zod version" concern for the dependency "zod": "^3.25.76" in package.json; remove that concern by either leaving the existing dependency as-is (since ^3.25.76 is a valid published 3.x release) or, if you intended to use Zod v4, update the package.json entry "zod": "^3.25.76" to a 4.x spec (for example "zod": "^4.0.0") and adjust any PR description/comment that claims the version is invalid to reflect the correct status.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/framework/preact/guides/form-groups.md`:
- Line 86: The sentence "Form groups have a distinct validation proceedure that
we think makes sense for sub-forms:" contains a typo; update the text to "Form
groups have a distinct validation procedure that we think makes sense for
sub-forms:" by replacing "proceedure" with "procedure" in the
docs/framework/preact/guides/form-groups.md content where that exact sentence
appears.
In `@docs/framework/react/guides/form-groups.md`:
- Line 86: Fix the typos in the documentation text: replace “proceedure” with
“procedure” and change “is ran” to “is run” wherever they appear (e.g., the
sentence starting "Form groups have a distinct validation proceedure..." and the
other instance noted at the second occurrence); update both occurrences to
correct spelling and grammar so the text reads "procedure" and "is run".
- Line 134: The fenced code block closing with the triple backticks currently
lacks a language tag; update that code fence to declare the language (e.g., add
"ts") so the block becomes a TypeScript fenced code block — locate the fenced
block around the closing ``` in the docs/framework/react/guides/form-groups.md
content and change the opening fence to ```ts to improve markdown linting and
readability.
In `@docs/framework/solid/guides/form-groups.md`:
- Line 89: Update the two grammar typos in the documentation: change the phrase
"validation proceedure" to "validation procedure" and change "is ran" to "is
run" wherever they occur (e.g., the sentence starting "Form groups have a
distinct validation proceedure..." and the line containing "is ran"); ensure
both occurrences are corrected to the proper spelling/tense.
- Around line 135-144: Add a language identifier to the fenced code block that
contains the definitions for step1Schema and schema by changing the opening ```
to ```tsx (or another appropriate language) so the block (the snippet defining
step1Schema, step2Schema and schema) gets proper syntax highlighting and
satisfies the MD040 lint rule.
In `@docs/framework/svelte/guides/form-groups.md`:
- Line 187: Change the grammatically incorrect phrase "is ran" to "is run" in
the sentence that references group.submissionAttempts (the text "It will treat
`group.submissionAttempts` as the way to change what validator is ran
before/after submit."); update that sentence to read something like "It will
treat `group.submissionAttempts` as the way to change what validator is run
before/after submit."
In `@docs/framework/vue/guides/form-groups.md`:
- Line 101: Fix two wording issues: correct the misspelling "proceedure" to
"procedure" and replace the grammatically incorrect "is ran" with "is run".
Locate the occurrences of the phrases (e.g., the sentence starting "Form groups
have a distinct validation proceedure..." and the other occurrence flagged) and
update them in the guide content and the second instance mentioned; ensure
surrounding punctuation/capitalization remains consistent.
In `@examples/angular/multi-step-wizard/src/index.html`:
- Line 5: Update the HTML document title element (the <title> tag) to a more
descriptive string that reflects the page content; replace the current "Simple"
title with something like "TanStack Form Angular Multi-Step Wizard" so the
browser tab and metadata clearly indicate this is the multi-step wizard example.
In `@examples/react/multi-step-wizard/index.html`:
- Line 9: Update the <title> element text so it reflects the correct example:
replace "TanStack Form React Simple Example App" with "TanStack Form React
Multi-Step Wizard" in the <title> tag to match the multi-step-wizard example.
In `@examples/react/multi-step-wizard/src/features/wizard/step2-subform.tsx`:
- Line 33: The Back button inside the form currently uses a bare <button> which
defaults to type="submit" and can trigger form submission; update the Back
button element (the one calling setStep(step - 1)) to explicitly set
type="button" so clicking it won't submit or validate the form.
---
Nitpick comments:
In `@docs/framework/preact/guides/form-groups.md`:
- Around line 134-143: The fenced code block containing the schema definitions
(const step1Schema, step2Schema, and const schema using z.object) should include
a language identifier for syntax highlighting; update the opening fence from ```
to ```typescript (or ```ts) so the block with step1Schema and schema is marked
as TypeScript.
In `@examples/lit/multi-step-wizard/package.json`:
- Line 15: The review flags an incorrect "invalid Zod version" concern for the
dependency "zod": "^3.25.76" in package.json; remove that concern by either
leaving the existing dependency as-is (since ^3.25.76 is a valid published 3.x
release) or, if you intended to use Zod v4, update the package.json entry "zod":
"^3.25.76" to a 4.x spec (for example "zod": "^4.0.0") and adjust any PR
description/comment that claims the version is invalid to reflect the correct
status.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: eb598f67-52e2-4a28-9bde-4bef92098edb
⛔ Files ignored due to path filters (3)
docs/assets/stepper.pngis excluded by!**/*.pngexamples/angular/multi-step-wizard/src/favicon.icois excluded by!**/*.icoexamples/react/multi-step-wizard/public/emblem-light.svgis excluded by!**/*.svg
📒 Files selected for processing (68)
docs/config.jsondocs/framework/angular/guides/form-groups.mddocs/framework/lit/guides/form-groups.mddocs/framework/preact/guides/form-groups.mddocs/framework/react/guides/form-groups.mddocs/framework/solid/guides/form-groups.mddocs/framework/svelte/guides/form-groups.mddocs/framework/vue/guides/form-groups.mdexamples/angular/multi-step-wizard/.editorconfigexamples/angular/multi-step-wizard/.gitignoreexamples/angular/multi-step-wizard/README.mdexamples/angular/multi-step-wizard/angular.jsonexamples/angular/multi-step-wizard/package.jsonexamples/angular/multi-step-wizard/src/app/app.component.tsexamples/angular/multi-step-wizard/src/app/shared-form.tsexamples/angular/multi-step-wizard/src/app/step1.component.tsexamples/angular/multi-step-wizard/src/app/step2.component.tsexamples/angular/multi-step-wizard/src/app/text-field.component.tsexamples/angular/multi-step-wizard/src/index.htmlexamples/angular/multi-step-wizard/src/main.tsexamples/angular/multi-step-wizard/tsconfig.app.jsonexamples/angular/multi-step-wizard/tsconfig.jsonexamples/angular/multi-step-wizard/tsconfig.spec.jsonexamples/lit/multi-step-wizard/.eslintrc.cjsexamples/lit/multi-step-wizard/.gitignoreexamples/lit/multi-step-wizard/README.mdexamples/lit/multi-step-wizard/index.htmlexamples/lit/multi-step-wizard/package.jsonexamples/lit/multi-step-wizard/src/components/text-field.tsexamples/lit/multi-step-wizard/src/features/wizard/page.tsexamples/lit/multi-step-wizard/src/features/wizard/shared-form.tsexamples/lit/multi-step-wizard/src/features/wizard/step1-subform.tsexamples/lit/multi-step-wizard/src/features/wizard/step2-subform.tsexamples/lit/multi-step-wizard/src/index.tsexamples/lit/multi-step-wizard/tsconfig.jsonexamples/preact/multi-step-wizard/README.mdexamples/preact/multi-step-wizard/index.htmlexamples/preact/multi-step-wizard/package.jsonexamples/preact/multi-step-wizard/src/App.tsxexamples/preact/multi-step-wizard/src/components/text-fields.tsxexamples/preact/multi-step-wizard/src/features/wizard/page.tsxexamples/preact/multi-step-wizard/src/features/wizard/shared-form.tsxexamples/preact/multi-step-wizard/src/features/wizard/step1-subform.tsxexamples/preact/multi-step-wizard/src/features/wizard/step2-subform.tsxexamples/preact/multi-step-wizard/src/hooks/form-context.tsxexamples/preact/multi-step-wizard/src/hooks/form.tsxexamples/preact/multi-step-wizard/src/index.tsxexamples/preact/multi-step-wizard/tsconfig.jsonexamples/preact/multi-step-wizard/vite.config.tsexamples/react/multi-step-wizard/.eslintrc.cjsexamples/react/multi-step-wizard/.gitignoreexamples/react/multi-step-wizard/README.mdexamples/react/multi-step-wizard/index.htmlexamples/react/multi-step-wizard/package.jsonexamples/react/multi-step-wizard/src/App.tsxexamples/react/multi-step-wizard/src/components/text-fields.tsxexamples/react/multi-step-wizard/src/features/wizard/page.tsxexamples/react/multi-step-wizard/src/features/wizard/shared-form.tsxexamples/react/multi-step-wizard/src/features/wizard/step1-subform.tsxexamples/react/multi-step-wizard/src/features/wizard/step2-subform.tsxexamples/react/multi-step-wizard/src/hooks/form-context.tsxexamples/react/multi-step-wizard/src/hooks/form.tsxexamples/react/multi-step-wizard/src/index.tsxexamples/react/multi-step-wizard/tsconfig.jsonexamples/react/next-server-actions-zod/package.jsonexamples/react/next-server-actions/package.jsonexamples/react/remix/package.jsonexamples/react/tanstack-start/package.json
✅ Files skipped from review due to trivial changes (27)
- examples/lit/multi-step-wizard/README.md
- examples/preact/multi-step-wizard/README.md
- examples/react/multi-step-wizard/README.md
- examples/angular/multi-step-wizard/src/main.ts
- examples/preact/multi-step-wizard/src/index.tsx
- examples/react/multi-step-wizard/.eslintrc.cjs
- examples/preact/multi-step-wizard/src/App.tsx
- examples/angular/multi-step-wizard/tsconfig.spec.json
- examples/react/next-server-actions-zod/package.json
- examples/react/remix/package.json
- examples/preact/multi-step-wizard/index.html
- examples/angular/multi-step-wizard/.editorconfig
- examples/react/multi-step-wizard/.gitignore
- examples/lit/multi-step-wizard/.eslintrc.cjs
- examples/react/multi-step-wizard/src/hooks/form-context.tsx
- examples/react/next-server-actions/package.json
- examples/react/multi-step-wizard/package.json
- examples/preact/multi-step-wizard/tsconfig.json
- examples/lit/multi-step-wizard/src/index.ts
- examples/react/multi-step-wizard/src/index.tsx
- examples/angular/multi-step-wizard/README.md
- examples/angular/multi-step-wizard/.gitignore
- examples/lit/multi-step-wizard/.gitignore
- examples/angular/multi-step-wizard/package.json
- examples/angular/multi-step-wizard/src/app/shared-form.ts
- examples/angular/multi-step-wizard/tsconfig.app.json
- examples/lit/multi-step-wizard/src/features/wizard/shared-form.ts
|
|
||
| ## Form Group Validation | ||
|
|
||
| Form groups have a distinct validation proceedure that we think makes sense for sub-forms: |
There was a problem hiding this comment.
Fix the typo.
"proceedure" should be "procedure".
📝 Proposed fix
-Form groups have a distinct validation proceedure that we think makes sense for sub-forms:
+Form groups have a distinct validation procedure that we think makes sense for sub-forms:📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Form groups have a distinct validation proceedure that we think makes sense for sub-forms: | |
| Form groups have a distinct validation procedure that we think makes sense for sub-forms: |
🧰 Tools
🪛 LanguageTool
[grammar] ~86-~86: Ensure spelling is correct
Context: ... Form groups have a distinct validation proceedure that we think makes sense for sub-forms...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/framework/preact/guides/form-groups.md` at line 86, The sentence "Form
groups have a distinct validation proceedure that we think makes sense for
sub-forms:" contains a typo; update the text to "Form groups have a distinct
validation procedure that we think makes sense for sub-forms:" by replacing
"proceedure" with "procedure" in the docs/framework/preact/guides/form-groups.md
content where that exact sentence appears.
|
|
||
| ## Form Group Validation | ||
|
|
||
| Form groups have a distinct validation proceedure that we think makes sense for sub-forms: |
There was a problem hiding this comment.
Fix spelling/grammar in validation wording.
Please change “proceedure” → “procedure” and “is ran” → “is run”.
Suggested edits
-Form groups have a distinct validation proceedure that we think makes sense for sub-forms:
+Form groups have a distinct validation procedure that we think makes sense for sub-forms:
...
-It will treat `group.submissionAttempts` as the way to change what validator is ran before/after submit.
+It will treat `group.submissionAttempts` as the way to change what validator is run before/after submit.Also applies to: 168-168
🧰 Tools
🪛 LanguageTool
[grammar] ~86-~86: Ensure spelling is correct
Context: ... Form groups have a distinct validation proceedure that we think makes sense for sub-forms...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/framework/react/guides/form-groups.md` at line 86, Fix the typos in the
documentation text: replace “proceedure” with “procedure” and change “is ran” to
“is run” wherever they appear (e.g., the sentence starting "Form groups have a
distinct validation proceedure..." and the other instance noted at the second
occurrence); update both occurrences to correct spelling and grammar so the text
reads "procedure" and "is run".
|
|
||
| > The reason we don't use the full path names for fields is so that you can compose your schemas like so: | ||
| > | ||
| > ``` |
There was a problem hiding this comment.
Add a language tag to the fenced code block.
The quoted code fence should declare a language for markdown lint/readability.
Suggested edit
-> ```
+> ```ts📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| > ``` | |
| > |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 134-134: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/framework/react/guides/form-groups.md` at line 134, The fenced code
block closing with the triple backticks currently lacks a language tag; update
that code fence to declare the language (e.g., add "ts") so the block becomes a
TypeScript fenced code block — locate the fenced block around the closing ``` in
the docs/framework/react/guides/form-groups.md content and change the opening
fence to ```ts to improve markdown linting and readability.
|
|
||
| ## Form Group Validation | ||
|
|
||
| Form groups have a distinct validation proceedure that we think makes sense for sub-forms: |
There was a problem hiding this comment.
Fix documentation grammar typos in validation text.
There are user-facing grammar mistakes:
- Line 89:
proceedure→procedure - Line 169:
is ran→is run
✍️ Suggested patch
-Form groups have a distinct validation proceedure that we think makes sense for sub-forms:
+Form groups have a distinct validation procedure that we think makes sense for sub-forms:-It will treat `group().submissionAttempts` as the way to change what validator is ran before/after submit.
+It will treat `group().submissionAttempts` as the way to change which validator is run before/after submit.Also applies to: 169-169
🧰 Tools
🪛 LanguageTool
[grammar] ~89-~89: Ensure spelling is correct
Context: ... Form groups have a distinct validation proceedure that we think makes sense for sub-forms...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/framework/solid/guides/form-groups.md` at line 89, Update the two
grammar typos in the documentation: change the phrase "validation proceedure" to
"validation procedure" and change "is ran" to "is run" wherever they occur
(e.g., the sentence starting "Form groups have a distinct validation
proceedure..." and the line containing "is ran"); ensure both occurrences are
corrected to the proper spelling/tense.
| > ``` | ||
| > const step1Schema = z.object({ | ||
| > name: z.string().min(2) | ||
| > }) | ||
| > | ||
| > const schema = z.object({ | ||
| > step1: step1Schema, | ||
| > step2: step2Schema | ||
| > }) | ||
| > ``` |
There was a problem hiding this comment.
Add a language identifier to the fenced code block.
The fenced block is missing a language tag (MD040), which hurts linting and syntax highlighting.
🧩 Suggested patch
-> ```
+> ```tsx
> const step1Schema = z.object({
> name: z.string().min(2)
> })
>
> const schema = z.object({
> step1: step1Schema,
> step2: step2Schema
> })
> ```🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 135-135: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/framework/solid/guides/form-groups.md` around lines 135 - 144, Add a
language identifier to the fenced code block that contains the definitions for
step1Schema and schema by changing the opening ``` to ```tsx (or another
appropriate language) so the block (the snippet defining step1Schema,
step2Schema and schema) gets proper syntax highlighting and satisfies the MD040
lint rule.
| <form.FormGroup name="step1" validators={{ onDynamic: step1Schema }} /> | ||
| ``` | ||
|
|
||
| It will treat `group.submissionAttempts` as the way to change what validator is ran before/after submit. |
There was a problem hiding this comment.
Fix grammar in the validator sentence.
“is ran” should be “is run” to avoid confusing wording in the guide.
Suggested edit
-It will treat `group.submissionAttempts` as the way to change what validator is ran before/after submit.
+It will treat `group.submissionAttempts` as the way to change what validator is run before/after submit.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| It will treat `group.submissionAttempts` as the way to change what validator is ran before/after submit. | |
| It will treat `group.submissionAttempts` as the way to change what validator is run before/after submit. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/framework/svelte/guides/form-groups.md` at line 187, Change the
grammatically incorrect phrase "is ran" to "is run" in the sentence that
references group.submissionAttempts (the text "It will treat
`group.submissionAttempts` as the way to change what validator is ran
before/after submit."); update that sentence to read something like "It will
treat `group.submissionAttempts` as the way to change what validator is run
before/after submit."
|
|
||
| ## Form Group Validation | ||
|
|
||
| Form groups have a distinct validation proceedure that we think makes sense for sub-forms: |
There was a problem hiding this comment.
Correct two wording issues in the guide.
There’s a spelling typo (“proceedure”) and a grammar issue (“is ran”).
Suggested edits
-Form groups have a distinct validation proceedure that we think makes sense for sub-forms:
+Form groups have a distinct validation procedure that we think makes sense for sub-forms:
...
-It will treat `formGroup.submissionAttempts` as the way to change what validator is ran before/after submit.
+It will treat `formGroup.submissionAttempts` as the way to change what validator is run before/after submit.Also applies to: 191-191
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/framework/vue/guides/form-groups.md` at line 101, Fix two wording
issues: correct the misspelling "proceedure" to "procedure" and replace the
grammatically incorrect "is ran" with "is run". Locate the occurrences of the
phrases (e.g., the sentence starting "Form groups have a distinct validation
proceedure..." and the other occurrence flagged) and update them in the guide
content and the second instance mentioned; ensure surrounding
punctuation/capitalization remains consistent.
| <html lang="en"> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <title>Simple</title> |
There was a problem hiding this comment.
Use a more descriptive title.
The title "Simple" doesn't convey that this is a multi-step wizard example. Consider "TanStack Form Angular Multi-Step Wizard" for clarity.
📝 Proposed fix
- <title>Simple</title>
+ <title>TanStack Form Angular Multi-Step Wizard</title>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <title>Simple</title> | |
| <title>TanStack Form Angular Multi-Step Wizard</title> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/angular/multi-step-wizard/src/index.html` at line 5, Update the HTML
document title element (the <title> tag) to a more descriptive string that
reflects the page content; replace the current "Simple" title with something
like "TanStack Form Angular Multi-Step Wizard" so the browser tab and metadata
clearly indicate this is the multi-step wizard example.
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
| <meta name="theme-color" content="#000000" /> | ||
|
|
||
| <title>TanStack Form React Simple Example App</title> |
There was a problem hiding this comment.
Update the title to match the example.
The title references "Simple Example App" but this is the multi-step-wizard example. Consider changing it to "TanStack Form React Multi-Step Wizard" for clarity.
📝 Proposed fix
- <title>TanStack Form React Simple Example App</title>
+ <title>TanStack Form React Multi-Step Wizard</title>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <title>TanStack Form React Simple Example App</title> | |
| <title>TanStack Form React Multi-Step Wizard</title> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/react/multi-step-wizard/index.html` at line 9, Update the <title>
element text so it reflects the correct example: replace "TanStack Form React
Simple Example App" with "TanStack Form React Multi-Step Wizard" in the <title>
tag to match the multi-step-wizard example.
| {(field) => <field.TextField label="Step 2 Name" />} | ||
| </form.AppField> | ||
|
|
||
| <button onClick={() => setStep(step - 1)}>Back</button> |
There was a problem hiding this comment.
Set the Back button type to button to prevent accidental submit.
Inside a <form>, a bare <button> submits by default, so Back can unintentionally trigger submit/validation.
Proposed fix
- <button onClick={() => setStep(step - 1)}>Back</button>
+ <button type="button" onClick={() => setStep(step - 1)}>
+ Back
+ </button>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button onClick={() => setStep(step - 1)}>Back</button> | |
| <button type="button" onClick={() => setStep(step - 1)}> | |
| Back | |
| </button> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/react/multi-step-wizard/src/features/wizard/step2-subform.tsx` at
line 33, The Back button inside the form currently uses a bare <button> which
defaults to type="submit" and can trigger form submission; update the Back
button element (the one calling setStep(step - 1)) to explicitly set
type="button" so clicking it won't submit or validate the form.
This PR implements #419 as outlined in the issue.
Core TODOs
corepackage integration testscorepackage type testsFormGroupgetFormGroupStoreor something like it so users can access group validity externallyFramework support
reactadapter base codereactadapter integration testsreactadapter type testsreactadapter examplesreactadapter docspreactadapter base codepreactadapter integration testspreactadapter type testspreactadapter examplespreactadapter docsangularadapter base codeangularadapter integration testsangularadapter examplesangularadapter docslitadapter base codelitadapter integration testslitadapter exampleslitadapter docssolidadapter base codesolidadapter integration testssolidadapter type testssolidadapter examplessolidadapter docssvelteadapter base codesvelteadapter integration testssvelteadapter examplessvelteadapter docsvueadapter base codevueadapter integration testsvueadapter examplesvueadapter docsSummary by CodeRabbit
New Features
Documentation
Tests