Skip to content

Conversation

@mhornbacher
Copy link
Contributor

🎯 Changes

Attempts to resolve #1907

Removes Math.random() introduced in #1893 from the hot path allowing the page to be pre-built with a a user provided form id.

source: https://nextjs.org/docs/app/getting-started/cache-components#non-deterministic-operations

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm test:pr.

🚀 Release Impact

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

Attempts to resolve TanStack#1907

Removes `Math.random()` introduced in TanStack#1893 from the hot path allowing the page to be pre-built with a a user provided form id.

source: https://nextjs.org/docs/app/getting-started/cache-components#non-deterministic-operations
@changeset-bot
Copy link

changeset-bot bot commented Dec 6, 2025

🦋 Changeset detected

Latest commit: 7546228

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

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

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

Copy link
Member

@crutchcorn crutchcorn left a comment

Choose a reason for hiding this comment

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

I'm unclear on if this is expected behavior, either.

At very least we cannot merge this PR because of the major bump it would trigger due to the changeset file.

Let's have @harry-whorlow and @AlemTuzlak look into this

@mhornbacher
Copy link
Contributor Author

mhornbacher commented Dec 6, 2025

@crutchcorn ah I must have misunderstood the PR instructions. I will update the changeset file :)

@crutchcorn
Copy link
Member

crutchcorn commented Dec 6, 2025

S'all good :) Changeset is a tricky tool to get used to

@mhornbacher mhornbacher closed this Dec 6, 2025
@mhornbacher mhornbacher reopened this Dec 6, 2025
@mhornbacher
Copy link
Contributor Author

Thank you so much for the pointer. Please let me know if changing it to patch is enough or if the file should be fully removed. It's my first contribution here and I am learning the ropes ;)

Also you mentioned that you are unclear if this is expected behavior either. I am not sure what that means...

I think the proper fix for this is to use useId which generates a consistent id based on the react component hierarchy and works with streaming. But that would break react 17 compatibility which is a major unjustified breaking change to support the cutting edge of only one, however popular, meta-framework (Next.JS 16).

@crutchcorn
Copy link
Member

I think the proper fix for this is to use useId

I think you're right...

But that would break react 17 compatibility

And also right here. What if we detect React.version in a custom useFormId hook and conditionally return useId or our uuid() implementation?

That shouldn't break the rules of React hook, especially if we do that replacement like so:

import {version as reactVersion, useId} from "react";

function useUUID() {
    /* UUID code here */
}

const useFormId = Number(reactVersion.split(".")[0]) > 17 ? useUUID : useId;

WDYT?

Aside about contributing

It's my first contribution here and I am learning the ropes

For the record, even if this PR isn't merged exactly as-is, please don't hesitate to find other tickets and/or reach out to be mentored (by me!) on how to continue contributing. We always enjoy and welcome new contributors.

@nx-cloud
Copy link

nx-cloud bot commented Dec 6, 2025

View your CI Pipeline Execution ↗ for commit 7546228

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

☁️ Nx Cloud last updated this comment at 2025-12-07 17:30:30 UTC

@nx-cloud
Copy link

nx-cloud bot commented Dec 6, 2025

View your CI Pipeline Execution ↗ for commit 9ec39bb


☁️ Nx Cloud last updated this comment at 2025-12-06 20:45:57 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 6, 2025

More templates

@tanstack/angular-form

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

@tanstack/form-core

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

@tanstack/form-devtools

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

@tanstack/lit-form

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

@tanstack/react-form

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

@tanstack/react-form-devtools

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

@tanstack/react-form-nextjs

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

@tanstack/react-form-remix

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

@tanstack/react-form-start

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

@tanstack/solid-form

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

@tanstack/solid-form-devtools

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

@tanstack/svelte-form

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

@tanstack/vue-form

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

commit: 7546228

@crutchcorn
Copy link
Member

@mhornbacher wanna test this in your project before we go live? :)

Check the install commands here: #1913 (comment)

Or by using:

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

And let me know if this fixed the bug. If it did, we'll merge and release ASAP

@mhornbacher
Copy link
Contributor Author

@crutchcorn yes. Sorry it took a minute, I wanted to isolate other possible causes but I can confirm that this does not fix the issue

Testing component (sorry for the ugly code)

I will have to dig deeper to find the cause. Are you ok if I use the CI pipeline here to push some experiments to try and isolate it? (do you pay for CI runs/package hosting or is it covered in a GitHub/StackBlitz OSS tier)

"use client";

import { useForm } from "@tanstack/react-form";

export function ForgotForm() {
  const form = useForm({}); // commenting out this line enables pre-rendering.
  return <>{'hi'}</>
}

Before and after removing const form = useForm({})

Screenshot 2025-12-06 at 4 10 57 PM Screenshot 2025-12-06 at 4 11 39 PM Screenshot 2025-12-06 at 4 13 11 PM

@crutchcorn
Copy link
Member

Glad we were able to confirm that this doesn't fix - that's better than pushing and being off-base.

Are you ok if I use the CI pipeline here to push some experiments to try and isolate it? (do you pay for CI runs/package hosting or is it covered in a GitHub/StackBlitz OSS tier)

Go for it! We don't pay for hosting, we're covered by OSS.

If you need any help debugging, let us know in the Discord and we'll help however we can :)

@mhornbacher
Copy link
Contributor Author

mhornbacher commented Dec 6, 2025

@crutchcorn I have not found the cause yet but I was actually able to confirm that uuid does not cause this at all (copied the code into my project and ran it)

However it does cause an annoying hydration issue and useId solves that :)

I went through each execution line in a debugger and am fully lost as to what could possibly be causing this. maybe a set of fresh eyes tomorrow will make it clear :(

@crutchcorn
Copy link
Member

crutchcorn commented Dec 6, 2025

Makes sense. I'm game to merge to fix the hydration issues.

Need anything from us before we merge for that purpose?

EDIT: I realized a problem. We can't import useId from React. We need to do React.useId and import React via import * from React otherwise we'll get runtime problems in 17

Copy link
Member

@crutchcorn crutchcorn left a comment

Choose a reason for hiding this comment

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

Sorry about so many reviews 😭

Noticed we need to make a new useUUID hook that returns a stable reference on 17, otherwise we'll have bugs.

@mhornbacher
Copy link
Contributor Author

Its all good. I do want to try and help fix the root problem if I can. I am more of an application and infrastructure engineer than a library maintainer so I am still getting used to the concerns of library development. Its a different world and its fun :)

For the project I am building it is really a non-issue because we can wrap the pages that need a form in a use-cache directive or a <Suspense /> tag is fine as the app is basically full of dynamic or per-user authenticated content anyway.

@mhornbacher
Copy link
Contributor Author

Sorry about so many reviews 😭

Noticed we need to make a new useUUID hook that returns a stable reference, otherwise we'll have bugs.

No problem. The original useState implementation should work right?

@crutchcorn
Copy link
Member

Yup! useState impl should be fine.

And agreed haha. Library code is very different but rewarding

@mhornbacher
Copy link
Contributor Author

I pushed a useMemo implementation (just seemed more correct than using a state that's setter is discarded).

I'll update it to the useState implementation now :)

@crutchcorn
Copy link
Member

Just for the future, useMemo is not guaranteed to run only once. useState with a function, on the other hand, is. :) That's why we went with it instead

@mhornbacher
Copy link
Contributor Author

Oh, I did not know that. Thank you!

@mhornbacher
Copy link
Contributor Author

mhornbacher commented Dec 6, 2025

I got somewhere! I setup a minimal reproduction within the monorepo and got it to spit out the exact function causing the issue. crypto.randomUUID() is called somewhere in throttle...

Sorry I am just overly excited

@crutchcorn
Copy link
Member

Oh! Interesting! We can't have crypto calls in our codebase because we need to support React Native as well, just FYI.

That is to say that when you find it, hunt it down and kill it with prejudice haha

@mhornbacher
Copy link
Contributor Author

mhornbacher commented Dec 6, 2025

Found it here in https://github.com/TanStack/pacer: https://github.com/TanStack/pacer/blob/6238bcf592edf01bb188ea0b412026692fc24513/packages/pacer/src/async-retryer.ts#L365-L383

It is related to the DevTools so likely not running in React Native at all :)

What is strange is that this is in the asyncRetry which should not be being called from the throttle function... 🤔

@mhornbacher
Copy link
Contributor Author

mhornbacher commented Dec 6, 2025

Verified that removing the import from @tanstack/pacer resolves the issue fully.

Does it make sense to remove it and replace it with a simple debounce to avoid bringing in crypto?

@crutchcorn
Copy link
Member

Let's tag in @KevinVandy who maintains Pacer, so we can patch upstream :)

1 similar comment
@crutchcorn
Copy link
Member

Let's tag in @KevinVandy who maintains Pacer, so we can patch upstream :)

@KevinVandy
Copy link
Member

This was fixed in Pacer a couple weeks ago. I thought @harry-whorlow might be updating soon?

Also, TanStack Form should install pacer-lite instead of pacer now for smaller bundle size.

@harry-whorlow
Copy link
Contributor

I have an open branch amt, just need to push it... Probably in the morning

@mhornbacher
Copy link
Contributor Author

@KevinVandy nice! I opened a PR to remove it entirely as its only used in one spot for dev-tools but it looks like you guys are on it!

#1916

@harry-whorlow
Copy link
Contributor

Pacer is updated in 1876 to pacer lite.

What was the issue with changesets?

@harry-whorlow
Copy link
Contributor

I think the proper fix for this is to use useId

Yeah, this breaks react 17, hence why we implemented our own uuid

And also right here. What if we detect React.version in a custom useFormId hook and conditionally return useId or our uuid() implementation?

Crazy, I didn’t realise you could import the react versions. I like this solution.

As for the changeset it looks like the perhaps the cli steps were misunderstood, since I can't see anything that would make Changesets bump a major. But yeah a patch version would suffice 😄

@mhornbacher
Copy link
Contributor Author

@harry-whorlow neither did I. @crutchcorn is a legend!

Do you need me to close this and open a new PR to flush out the major version bump?

@crutchcorn
Copy link
Member

@harry-whorlow the issue with patchsets have been updated since my comment :) A bit of back-n-forth yesterday, especially cuz I was on mobile out on the town reviewing.

@mhornbacher nope! This looks great to me; merging now after workflows pass.

@codecov
Copy link

codecov bot commented Dec 7, 2025

Codecov Report

❌ Patch coverage is 50.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.50%. Comparing base (6892ed0) to head (7546228).
⚠️ Report is 88 commits behind head on main.

Files with missing lines Patch % Lines
packages/react-form/src/useUUID.ts 0.00% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #1913       +/-   ##
===========================================
- Coverage   90.35%   54.50%   -35.85%     
===========================================
  Files          38       18       -20     
  Lines        1752      233     -1519     
  Branches      444       34      -410     
===========================================
- Hits         1583      127     -1456     
+ Misses        149       94       -55     
+ Partials       20       12        -8     

☔ 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.

@crutchcorn crutchcorn merged commit 6a5e1c1 into TanStack:main Dec 7, 2025
5 of 11 checks passed
@crutchcorn
Copy link
Member

This is merged into main! Now we'll wait for @harry-whorlow to merge their PR to pacer-lite and we'll cut a release

@github-actions github-actions bot mentioned this pull request Dec 7, 2025
@mhornbacher
Copy link
Contributor Author

Thank you guys so very much. Your dedication to this project and its performance for the wider web is above and beyond what anyone could imagine to ask for ❤️

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.

4 participants