Skip to content

Conversation

@Kyujenius
Copy link
Contributor

🎯 Changes

Fixes : #1973

This PR is built on the fundamental premise that "The most specific scope (Field) must always take precedence over the generic scope (Form)." Similar to variable shadowing in programming or specificity in CSS, an explicit declaration at the field level represents a stronger developer intent than a global default.

Currently, the lack of a clear hierarchy leads to architectural inconsistencies:

  1. The Paradox: isDefaultValue might claim a value is "default," yet reset() restores a completely different value from the form-level config.
  2. The False Positive: Clearing a field to undefined incorrectly triggers isDefaultValue: true even when explicit defaults are defined at the form level.

By unifying isDefaultValue, form.reset(), and form.resetField() under a single Prioritization Strategy (Field over Form), this PR ensures that TanStack Form behaves as a predictable, high-integrity state machine.

Introduction of Prioritized Default System

This PR introduces a unified prioritization strategy where Field-level defaults always override Form-level defaults.

Key Changes:

  1. isDefaultValue Determination: Now uses a single prioritized default value (Field ?? Form) for comparison instead of a logical OR.
  2. form.reset() Consistency: Now merges form-level defaults with currently mounted field-level defaults, preventing the "reset betrayal" where values would unexpectedly change to form-defaults.
  3. form.resetField() Consistency: Updated to respect the same priority when resetting individual fields.

Fixed Scenarios:

  • Scenario 1: When a default value is specified, setting the field to undefined now correctly sets isDefaultValue: false.
  • Scenario 2: When form-level and field-level defaults differ, both isDefaultValue and reset() now consistently follow the field-level default.

⚠️ I Updated Existing Tests

I’d like to verify if this matches what the maintainers had in mind.

Modified current test cases in FieldApi.spec.ts that were previously based on the incorrect logical OR assumption. These tests were updated to reflect the new prioritized logic, ensuring that isDefaultValue only returns true for the actual value that form.reset() would restore.

isDefaultValue Check Logic AS-IS / TO-BE

AS-IS TO-BE
image image

resset() Logic AS-IS / TO-BE

AS-IS TO-BE
image image

IMO

I realize this shifts the precedence closer to a 'Variable Shadowing' model. If this is too big of a breaking change, I'm happy to discuss putting this behind a flag or finding a middle ground. My main goal is to ensure reset() and isDefaultValue rely on the same logic. I believe this prioritization makes the library significantly more predictable. By aligning the internal logic with how developers naturally think about scope and specificity,

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm test:pr. (Verified form-core tests and custom regression tests)

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

@changeset-bot
Copy link

changeset-bot bot commented Jan 25, 2026

🦋 Changeset detected

Latest commit: 53da788

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@tanstack/form-core Minor
@tanstack/angular-form Minor
@tanstack/form-devtools Patch
@tanstack/lit-form Patch
@tanstack/react-form Minor
@tanstack/solid-form Minor
@tanstack/svelte-form Minor
@tanstack/vue-form Minor
@tanstack/react-form-devtools Patch
@tanstack/solid-form-devtools Patch
@tanstack/react-form-nextjs Minor
@tanstack/react-form-remix Minor
@tanstack/react-form-start Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@nx-cloud
Copy link

nx-cloud bot commented Jan 25, 2026

View your CI Pipeline Execution ↗ for commit 53da788

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 1m 27s View ↗
nx run-many --target=build --exclude=examples/** ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-25 16:51:05 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 25, 2026

More templates

@tanstack/angular-form

npm i https://pkg.pr.new/@tanstack/angular-form@2006

@tanstack/form-core

npm i https://pkg.pr.new/@tanstack/form-core@2006

@tanstack/form-devtools

npm i https://pkg.pr.new/@tanstack/form-devtools@2006

@tanstack/lit-form

npm i https://pkg.pr.new/@tanstack/lit-form@2006

@tanstack/react-form

npm i https://pkg.pr.new/@tanstack/react-form@2006

@tanstack/react-form-devtools

npm i https://pkg.pr.new/@tanstack/react-form-devtools@2006

@tanstack/react-form-nextjs

npm i https://pkg.pr.new/@tanstack/react-form-nextjs@2006

@tanstack/react-form-remix

npm i https://pkg.pr.new/@tanstack/react-form-remix@2006

@tanstack/react-form-start

npm i https://pkg.pr.new/@tanstack/react-form-start@2006

@tanstack/solid-form

npm i https://pkg.pr.new/@tanstack/solid-form@2006

@tanstack/solid-form-devtools

npm i https://pkg.pr.new/@tanstack/solid-form-devtools@2006

@tanstack/svelte-form

npm i https://pkg.pr.new/@tanstack/svelte-form@2006

@tanstack/vue-form

npm i https://pkg.pr.new/@tanstack/vue-form@2006

commit: 53da788

@Kyujenius Kyujenius force-pushed the fix/isDefaultValue-logic branch from 3253468 to 9dead10 Compare January 25, 2026 16:29
@sentry
Copy link

sentry bot commented Jan 25, 2026

Codecov Report

❌ Patch coverage is 94.73684% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 89.61%. Comparing base (6892ed0) to head (53da788).
⚠️ Report is 126 commits behind head on main.

Files with missing lines Patch % Lines
packages/form-core/src/FormApi.ts 94.73% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2006      +/-   ##
==========================================
- Coverage   90.35%   89.61%   -0.75%     
==========================================
  Files          38       48      +10     
  Lines        1752     1964     +212     
  Branches      444      496      +52     
==========================================
+ Hits         1583     1760     +177     
- Misses        149      183      +34     
- Partials       20       21       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@LeCarbonator LeCarbonator left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks clean! I'll need to do some additional checks though, since this is potentially a breaking change instead of a fix.

It may take a few days. Thanks for tackling the issue!

Comment on lines +2561 to +2562
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
this.getFieldInfo(field)?.instance?.options.defaultValue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lint error seems odd. Which part does it think does not need an optional chain?

From a surface level, this looks like a wrong type that should be adjusted to reflect (potential) runtime values.


field.setValue('test')
expect(field.getMeta().isDefaultValue).toBe(true)
expect(field.getMeta().isDefaultValue).toBe(false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminder to self: Check git blame.

I don't this change is good, but I want to know the context of why it was explicitly listed as unit test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

isDefaultValue is true when field value is undefined, and also for two distinct values

2 participants