Skip to content

Conversation

@jacekradko
Copy link
Member

@jacekradko jacekradko commented Feb 10, 2026

Summary

  • Suppress intermediate state emissions from updateClient when called during setActive (e.g. from touch()'s piggybacked client response)
  • Prevents React components from seeing the new orgId before transitive state (undefined) is set during org switches
  • Uses the existing __internal_setActiveInProgress flag to gate #emit() in updateClient

Root Cause

During setActive, touch() triggers updateClient() via BaseResource._baseFetch client piggybacking, which calls #emit() before #setTransitiveState() runs. With the migration from useState+addListener to useSyncExternalStore, each emission causes a synchronous re-render (sync-priority lane updates to prevent tearing). This exposed the new orgId to components before the transitive state was set, causing flickering during org switches.

Emission sequence before fix: updateClient#emit(newOrg)setTransitiveState#emit(undefined)#updateAccessors#emit(newOrg)
Emission sequence after fix: setTransitiveState#emit(undefined)#updateAccessors#emit(newOrg)

Test plan

  • Added test: verifies no intermediate orgId emission before transitive state when navigating
  • Added test: verifies single emission (no duplicate) when switching orgs without navigation
  • Both tests fail without the fix, pass with it
  • All existing clerk-js tests pass (99 tests)
  • pnpm build passes

Summary by CodeRabbit

  • Bug Fixes

    • Prevented premature/undefined organization state emissions during active organization switches, avoiding transient incorrect state being observed by listeners and preventing unnecessary intermediate updates.
  • Tests

    • Added tests verifying organization-switching no longer emits intermediate undefined states and that listeners only receive the final, stable organization state.

…ng setActive

During setActive, the touch() call triggers a piggybacked client update
via BaseResource._baseFetch, which calls updateClient() and emits state
to React before setTransitiveState() runs. With useSyncExternalStore
(unlike the old useState+addListener approach), each emission causes a
synchronous re-render. This exposed the new orgId to components before
the transitive state (undefined) was set, causing flickering and stale
data issues during org switches.

Gate the #emit() in updateClient with the existing
__internal_setActiveInProgress flag so intermediate state from
piggybacked client updates is suppressed. setActive emits the final
state itself via #setTransitiveState or #updateAccessors.
@vercel
Copy link

vercel bot commented Feb 10, 2026

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

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Feb 10, 2026 10:24pm

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Feb 10, 2026

🦋 Changeset detected

Latest commit: f47d344

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

This PR includes changesets to release 3 packages
Name Type
@clerk/clerk-js Patch
@clerk/chrome-extension Patch
@clerk/expo 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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 10, 2026

📝 Walkthrough

Walkthrough

A guard was added to Clerk's setActive flow to suppress synchronous emissions while __internal_setActiveInProgress is true, preventing intermediate state emissions when client updates occur during an active organization switch. Two tests were added verifying no intermediate undefined or transitive-emission occurs when updateClient is invoked during setActive (with and without navigation). A changeset file documents a patch release.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main fix: suppressing intermediate emissions from updateClient during setActive, which is the core change across the codebase modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


No actionable comments were generated in the recent review. 🎉


Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 10, 2026

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7815

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7815

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7815

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7815

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7815

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7815

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@7815

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7815

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7815

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7815

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7815

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7815

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7815

@clerk/react

npm i https://pkg.pr.new/@clerk/react@7815

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7815

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7815

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7815

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7815

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@7815

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7815

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7815

commit: f47d344

@jacekradko
Copy link
Member Author

!snapshot

@clerk-cookie
Copy link
Collaborator

Hey @jacekradko - the snapshot version command generated the following package versions:

Package Version
@clerk/agent-toolkit 0.3.0-snapshot.v20260210223332
@clerk/astro 3.0.0-snapshot.v20260210223332
@clerk/backend 3.0.0-snapshot.v20260210223332
@clerk/chrome-extension 3.0.0-snapshot.v20260210223332
@clerk/clerk-js 6.0.0-snapshot.v20260210223332
@clerk/dev-cli 1.0.0-snapshot.v20260210223332
@clerk/expo 3.0.0-snapshot.v20260210223332
@clerk/expo-passkeys 1.0.0-snapshot.v20260210223332
@clerk/express 2.0.0-snapshot.v20260210223332
@clerk/fastify 2.7.0-snapshot.v20260210223332
@clerk/localizations 4.0.0-snapshot.v20260210223332
@clerk/msw 0.0.1-snapshot.v20260210223332
@clerk/nextjs 7.0.0-snapshot.v20260210223332
@clerk/nuxt 2.0.0-snapshot.v20260210223332
@clerk/react 6.0.0-snapshot.v20260210223332
@clerk/react-router 3.0.0-snapshot.v20260210223332
@clerk/shared 4.0.0-snapshot.v20260210223332
@clerk/tanstack-react-start 1.0.0-snapshot.v20260210223332
@clerk/testing 2.0.0-snapshot.v20260210223332
@clerk/ui 1.0.0-snapshot.v20260210223332
@clerk/upgrade 2.0.0-snapshot.v20260210223332
@clerk/vue 2.0.0-snapshot.v20260210223332

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/agent-toolkit

npm i @clerk/agent-toolkit@0.3.0-snapshot.v20260210223332 --save-exact

@clerk/astro

npm i @clerk/astro@3.0.0-snapshot.v20260210223332 --save-exact

@clerk/backend

npm i @clerk/backend@3.0.0-snapshot.v20260210223332 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.0.0-snapshot.v20260210223332 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.0.0-snapshot.v20260210223332 --save-exact

@clerk/dev-cli

npm i @clerk/dev-cli@1.0.0-snapshot.v20260210223332 --save-exact

@clerk/expo

npm i @clerk/expo@3.0.0-snapshot.v20260210223332 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.0.0-snapshot.v20260210223332 --save-exact

@clerk/express

npm i @clerk/express@2.0.0-snapshot.v20260210223332 --save-exact

@clerk/fastify

npm i @clerk/fastify@2.7.0-snapshot.v20260210223332 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.0.0-snapshot.v20260210223332 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.1-snapshot.v20260210223332 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.0.0-snapshot.v20260210223332 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.0.0-snapshot.v20260210223332 --save-exact

@clerk/react

npm i @clerk/react@6.0.0-snapshot.v20260210223332 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.0.0-snapshot.v20260210223332 --save-exact

@clerk/shared

npm i @clerk/shared@4.0.0-snapshot.v20260210223332 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.0.0-snapshot.v20260210223332 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.0-snapshot.v20260210223332 --save-exact

@clerk/ui

npm i @clerk/ui@1.0.0-snapshot.v20260210223332 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.0-snapshot.v20260210223332 --save-exact

@clerk/vue

npm i @clerk/vue@2.0.0-snapshot.v20260210223332 --save-exact

@jacekradko jacekradko requested review from Ephem and brkalow February 11, 2026 03:44
@jacekradko jacekradko marked this pull request as draft February 12, 2026 03:34
@jacekradko jacekradko marked this pull request as draft February 12, 2026 03:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants