TanStack / TanStack/form

Using zod schema as a form level onChange validator causes form elements to unnecessarily re-render.

Open
#1,625 6 comments 20 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

v1 v2: needs investigation
Dominant language
TypeScript
Stars
6.7k
Forks
682
Avg merge
5d 18h
Merged PRs (30d)
7

Description

Describe the bug

Let's consider the example from the documentation which uses a zod schema as its form-level onChange validator:

https://tanstack.com/form/latest/docs/framework/react/examples/standard-schema

https://6ymfgt-3001.csb.app/

const ZodSchema = z.object({
  firstName: z
    .string()
    .min(3, '[Zod] You must have a length of at least 3')
    .startsWith('A', "[Zod] First name must start with 'A'"),
  lastName: z.string().min(3, '[Zod] You must have a length of at least 3'),
})

export default function App() {
  const form = useForm({
    validators: {
      onChange: ZodSchema,
    },
  })

  return (
    ///
  )
}

https://github.com/user-attachments/assets/b42e6208-6650-4166-9b48-cb0e2ccf180d

Initially, typing in either of the inputs will cause both of them to re-render on each keystroke. This is because the onChange validator seemingly triggers on each keystroke, and each field which contains an error will be re-rendered. I speculate this is because the internal fieldMeta gets updated, and that's causing this issue. But as soon as any of the inputs no longer contains invalid data, it stops getting re-rendered.

That would be okay in some scenarios, but what if you want to trigger validation only on fields that have isDirty: true state, or in other words, fields that users have already interacted with. In most cases it makes no sense to validate and display errors if the user have provided no input yet.

Most importantly, this particular behavior isn't documented anywhere. I basically spent several hours trying to pinpoint the exact issue until I was able to figure out what is going on.

Here is where things get extra confusing. Let's try to mimic the zod validation by writing our own simple validator like this:

export default function App() {
  const form = useForm({
    validators: {
      onChange: ({ value }) => {
        return {
          fields: {
            ...value.firstName.length < 3 && {
              firstName: '[Custom] You must have a length of at least 3'
            },
            ...!value.firstName.startsWith('A') && {
              firstName: "[Custom] First name must start with 'A'"
            },
            ...value.lastName.length < 3 && {
              lastName: '[Custom] You must have a length of at least 3'
            },
          }
        }
       
      }
    },
  })

  return (
    //
  )
}

Now the problem is gone. All we ever did was remove the nested message property from the error map and return the error as string instead. Why that works? I have no clue.

https://github.com/user-attachments/assets/6ee74678-43a1-4d39-8ed3-60380aa692cb

Your minimal, reproducible example

https://codesandbox.io/p/devbox/gallant-stitch-5sqhyv

Steps to reproduce

Reproduction examples shown above.

Expected behavior

Form-level validators onChange validators should not cause all elements to re-render, and/or there should be an option to configure the default behavior. Most importantly, this should be documented somewhere, and if it's the intended behavior then it should be listed as a limitation.

Currently on crossroads not knowing what to do and if I should go back to RHF because this is kind of a deal-breakers for my particular use case.

Cheers.

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform
  • OS: Linux
  • Browser: Firefox/Chromium
TanStack Form adapter

react-form

TanStack Form version

1.14.2

TypeScript version

No response

Additional context

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the React standard-schema documentation example and the linked CodeSandbox reproduction, comparing the Zod form-level onChange validator with the custom validator. Trace the form and React adapter update paths to identify why invalid field errors trigger extra renders; done means preventing unnecessary re-renders or documenting the intended limitation and validating the behavior with a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.