Skip to content

Conversation

@jacekradko
Copy link
Member

Summary

Replaces the flushSync call in BaseRouter's baseNavigate with a deferred promise pattern.

Problem

The current implementation uses flushSync to guarantee the re-render happens before returning control to the caller. While this works, flushSync has performance implications:

  • Forces React to synchronously flush the entire update queue
  • Bypasses React's batching and scheduling optimizations
  • Can cause layout thrashing in complex UIs

Solution

Use a deferred promise pattern instead:

  1. Store the resolve function in state alongside the new route parts
  2. An effect resolves the promise after React commits the state update
  3. This achieves the same guarantee without forcing synchronous updates
// Before (flushSync)
flushSync(() => {
  setRouteParts({ path: toURL.pathname, queryString: toURL.search });
});
return internalNavRes;

// After (deferred promise)
return new Promise(resolve => {
  setRouteParts({ path: toURL.pathname, queryString: toURL.search });
  setPendingNavigation({
    routeParts: { path: toURL.pathname, queryString: toURL.search },
    result: internalNavRes,
    resolve,
  });
});

Benefits

  • Avoids flushSync performance penalty
  • Works better with React concurrent features
  • Maintains the same behavioral guarantee (re-render completes before promise resolves)

Replace the flushSync call in BaseRouter's baseNavigate with a deferred
promise pattern. This achieves the same guarantee (re-render completes
before returning control to the caller) without the performance penalty
of synchronously flushing React's update queue.

The new approach stores the resolve function in state alongside the new
route parts, and an effect resolves the promise after React commits the
state update.
@changeset-bot
Copy link

changeset-bot bot commented Jan 13, 2026

⚠️ No Changeset found

Latest commit: c418183

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vercel
Copy link

vercel bot commented Jan 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jan 13, 2026 5:18pm

@jacekradko jacekradko changed the title refactor(ui): replace flushSync with deferred promise pattern feat(ui): replace flushSync with deferred promise pattern Jan 13, 2026
@jacekradko jacekradko changed the title feat(ui): replace flushSync with deferred promise pattern feat(ui): replace sync update with deferred promise pattern Jan 13, 2026
@jacekradko jacekradko requested a review from Ephem January 13, 2026 17:50
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.

2 participants