You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
canSubmit is always true on first render of form #723
I am using a form with the zodValidator and have several field-level validations. On the first render the canSubmit property is true even though several fields are invalid. After changing the first input the validations run and canSubmit is invalid.
The Submit button is enabled even though the firstName has a minLength validation of 3
Type something in any of the fields
The Submit button is disabled until all the validations are correct
Expected behavior
I would expect that the canSubmit property considers all validations on the first render and shows a disabled Submit button from the start instead of changing from enabled to disabled after typing one letter and going back to enabled after all validations are correct.
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
OS: MacOS
Browser: Arc
TanStack Form adapter
react-form
TanStack Form version
0.20.2
TypeScript version
No response
Additional context
No response
Reacted by Luke Glazebrook, Michael Beemer, Christoph Schmatzler, Niels Morf, Elden Park, Daniel Jung, Tobias Büschel, Ondřej Misák, Evert De Spiegeleer, MGEJ and 5 more
Hey! The current behavior in the example seems correct to me, the validator is triggered onChange hence I'd expect no validation to be run as long as the value doesn't change.
What you're trying to achieve might be validating onMount.
With that said, if we agree that onMount is the answer, I noticed that there might be a bug there preventing canSubmit to go false but it's another topic we might want to tackle (and I already have an idea why we have this bug).
An alternative option could be to manually run form.validateAllFields("change") when your form mounts.
Reacted by Noah Kurz and Luke Randolph
Reacted by Elkin Alberto, Andrei Hudovich, johnson-enerdefi and Florian Kozel
I'm having the same issue. I'm trying to run validations at onChange (tried both at the form level and at the field level). The form is initially valid, both canSubmit and isValid are true, even though all fields are empty and thus invalid. Only when changing one of the values the form state is changed to invalid and canSubmit is false.
Using onMount for the initial validation doesn't help as this only runs when the form is mounted, causing the form to stay invalid even when fields are populated and the state changes.
All I want is to have the submit button disabled until the form is actually valid, which is a very simple and very common use case. I'm very surprised that it's so hard to achieve.
Hoping for a fix soon... 🙏
Thx
Reacted by Elkin Alberto, Ian Weston, Brandon Ryan, Tobias Büschel, Yuki Masaki, Janne Kanerva, Marian Šimeček, John Cramb, non-earnin, trompx and 6 more
Opinion:
Its the fields responsibility to choose whether or not to display the error, and the field indeed should be in error state on mount. The field can choose to only display its error when the component has been touched.
<Fieldname="resource"validators={{onMount: myValidator,onChange: myValidator,}}children={field=>(<MyCustomInputComponent//...other field propserrors={field.state.meta.isTouched ? field.state.meta.errors : []}/>)}/>
Then just use the same validator for onChange and onMount.
The fact that you have to specify the same function for onChange and onMount is a bit annoying for sure.
I have a helper function I use to simplify this for me. Just takes a function and returns the object form that tanstack is expecting.
Correction: The above strategy wont work because onMount validator seems to mark the field as touched. I definitely think this is a bug. @Balastrong opinion here?
As mentioned at the beginning, it's correct that if there's only the onChange validator the form has canSubmit: true. Before any interaction the validator has never ran, thus the form is valid.
The recommended way is to run validation onMount too, but was broken until today as validation errors didn't go away. Now that #726 has been merged (update to version 0.34.4) you can safely use it, errors will go away as soon as a field is touched 🙌
One tiny detail remains, to have a first validation when the component mounts and keep validating when values are changed, you have to set the same validator to both onMount and onChange. Not a big deal but do you think we need a simple API to declare that and let form handle that internally? Let's discuss in #1005
Also, sorry if this took so long and thanks for all the feedback and examples!
With that you do not need to duplicate your validator into onMount. You just don't rely on the OOTB state.canSubmit and make up your own with a selector.
Reacted by Gregory Kalinowski, Majid Sajadi, Rodrigo Mahamud García, Kiran Kunigiri, lyeslimani, Axel, Andrei Hudovich, Samuel García and Paweł Chabros
I just wanted to point out this alternative solution as well:
<form.Subscribe selector={(state) => state.isValid && !state.isPristine}>
{(canSubmit) => Submit}
</form.Subscribe>
With that you do not need to duplicate your validator into onMount. You just don't rely on the OOTB state.canSubmit and make up your own with a selector.
With each passing hour I'm regretting some of my choices, but am now in quite deep.
In my case, I have a <select /> element that I just cannot get to show error states. Even worse, the field is unexpectedly "touched", meaning that the form can be submitted without a value in the field.
To then make things worse still, I'm seeing that despite passing a defaultValue of empty string that I might be able to validate against in useActionState, the select is taking up the value of the first <option />, which I should be able to escape according to the Radix docs. From what I gather, this is happening somewhere in the Tanstack Form context, and not the Radix context..., i.e. the value is "" at the point of submission but becomes the first option by the time tanstack hands it off.
Anyway, your solution gave me the idea to just lockdown the form so that the user can't submit it until the required fields are both touched and dirty.
// inside my form button<form.Subscribeselector={(formState)=>{constrequiredFieldsComplete=requiredFields?.map((field)=>formState.fieldMeta[field]?.isTouched&&formState.fieldMeta[field]?.isDirty);returnformState.isValid&&!requiredFieldsComplete?.includes(false);}}children={(canSubmit)=>(
...
// usage in my form...<form.AppForm><form.FormActions/* actionText={not important)} */requiredFields={['foo','bar']}/> // names of fields
</form.AppForm>
I really don't like having to do this, and would just jump ship if I wasn't staring down at a heavy deadline because some of these edges have really tested me the last few days.
I faced the same problem on the first render. But after reading the helpful comments above and referring to the field State docs, I came up with this solution:
/** * Determines the form submission status based on the provided form state. * * @param state - The form state object from TanStack Form. * @returns An object containing: * - canSubmit: A boolean indicating if the form can be submitted, which is true when the form is valid, has been modified, and can be submitted. * - isSubmitting: A boolean indicating if the form is currently in the process of being submitted. */constgetFormSubmissionStatus=(state: AnyFormState)=>({canSubmit: state.isValid&&!state.isPristine&&state.canSubmit,isSubmitting: state.isSubmitting,});//---------------- <form.Subscribeselector={getFormSubmissionStatus}children={({canSubmit, isSubmitting})=>{return(<Buttondisabled={!canSubmit}loading={isSubmitting}>
text
</Button>);}}/>
When using SSR (TanStack Start), the button is still not disabled before hydration. Both onMount and onChange validator are set to a same scheme. It seems that canSubmit is always true when rendered on the server side.
Activity
Balastrong commentedon May 29, 2024
Hey! The current behavior in the example seems correct to me, the validator is triggered
onChangehence I'd expect no validation to be run as long as the value doesn't change.What you're trying to achieve might be validating
onMount.With that said, if we agree that
onMountis the answer, I noticed that there might be a bug there preventingcanSubmitto go false but it's another topic we might want to tackle (and I already have an idea why we have this bug).An alternative option could be to manually run
form.validateAllFields("change")when your form mounts.afriddev commentedon Jun 19, 2024
** I figured out the solution for
canSubmitreturntruewhen your component renders,you need to validate all your form.Filed when it mounts **function runOnMount({ value }: any) { form.validateAllFields("change"); return value; }validators={{ onChange: (value) => { return handleMobileNumberChange(value?.value); }, onMount: runOnMount, }}<form.Subscribe selector={(state) => [state.canSubmit, state.isSubmitting]} > {([canSubmit, isSubmitting]) => { return ( <Button disabled={!canSubmit} type="submit"> {!isSubmitting ? (!edit ? SUBMIT : MODIFY) : "Submiting ..."} </Button> ); }} </form.Subscribe>Glazy commentedon Jul 6, 2024
Not sure what the answer is here but I also encountered this same behaviour and find it confusing.
I validate my fields
onChange(and sometimesonBlur) but I don't count the form as valid until every field has successfully ran it's validation(s).I would've expected that disabling the "Submit" button based on
isValidorcanSubmitwould be sufficient for this usage.OPerel commentedon Sep 22, 2024
I'm having the same issue. I'm trying to run validations at
onChange(tried both at the form level and at the field level). The form is initially valid, bothcanSubmitandisValidare true, even though all fields are empty and thus invalid. Only when changing one of the values the form state is changed to invalid andcanSubmitis false.Using
onMountfor the initial validation doesn't help as this only runs when the form is mounted, causing the form to stay invalid even when fields are populated and the state changes.All I want is to have the submit button disabled until the form is actually valid, which is a very simple and very common use case. I'm very surprised that it's so hard to achieve.
Hoping for a fix soon... 🙏
Thx
brandonryan commentedon Oct 29, 2024
Opinion:
Its the fields responsibility to choose whether or not to display the error, and the field indeed should be in error state on mount. The field can choose to only display its error when the component has been touched.
Then just use the same validator for onChange and onMount.
The fact that you have to specify the same function for onChange and onMount is a bit annoying for sure.
I have a helper function I use to simplify this for me. Just takes a function and returns the object form that tanstack is expecting.
brandonryan commentedon Oct 29, 2024
Correction: The above strategy wont work because onMount validator seems to mark the field as touched. I definitely think this is a bug. @Balastrong opinion here?
Balastrong commentedon Nov 4, 2024
Hey everyone, an update on this issue!
As mentioned at the beginning, it's correct that if there's only the
onChangevalidator the form hascanSubmit: true. Before any interaction the validator has never ran, thus the form is valid.The recommended way is to run validation
onMounttoo, but was broken until today as validation errors didn't go away. Now that #726 has been merged (update to version 0.34.4) you can safely use it, errors will go away as soon as a field is touched 🙌One tiny detail remains, to have a first validation when the component mounts and keep validating when values are changed, you have to set the same validator to both
onMountandonChange. Not a big deal but do you think we need a simple API to declare that and let form handle that internally? Let's discuss in #1005Also, sorry if this took so long and thanks for all the feedback and examples!
johanfive commentedon Feb 24, 2025
I just wanted to point out this alternative solution as well:
With that you do not need to duplicate your validator into
onMount. You just don't rely on the OOTBstate.canSubmitand make up your own with aselector.akin-fagbohun commentedon Apr 26, 2025
With each passing hour I'm regretting some of my choices, but am now in quite deep.
In my case, I have a
<select />element that I just cannot get to show error states. Even worse, the field is unexpectedly "touched", meaning that the form can be submitted without a value in the field.To then make things worse still, I'm seeing that despite passing a defaultValue of empty string that I might be able to validate against in useActionState, the select is taking up the value of the first
<option />, which I should be able to escape according to the Radix docs. From what I gather, this is happening somewhere in the Tanstack Form context, and not the Radix context..., i.e. the value is""at the point of submission but becomes the first option by the time tanstack hands it off.Anyway, your solution gave me the idea to just lockdown the form so that the user can't submit it until the required fields are both touched and dirty.
I really don't like having to do this, and would just jump ship if I wasn't staring down at a heavy deadline because some of these edges have really tested me the last few days.
dasezo commentedon Jul 30, 2025
I faced the same problem on the first render. But after reading the helpful comments above and referring to the field State docs, I came up with this solution:
balthild commentedon Mar 26, 2026
When using SSR (TanStack Start), the button is still not disabled before hydration. Both
onMountandonChangevalidator are set to a same scheme. It seems thatcanSubmitis always true when rendered on the server side.