Repository navigation
useAuth() return object identity churn forces Convex/React re-render cascades #10413
Description
Preliminary Checks
-
I have reviewed the documentation: https://clerk.com/docs
-
I have searched for existing issues: https://github.com/clerk/javascript/issues
-
I have not already reached out to Clerk support via email or Discord (if you have, no need to open an issue here)
-
This issue is not a question, general help request, or anything other than a bug report directly related to Clerk. Please ask questions in our Discord community: https://clerk.com/discord.
Reproduction
https://github.com/luchillo17/clerk-useauth-identity-repro
Publishable key
pk_test_cmVzb2x2ZWQtZ2xpZGVyLTYuY2xlcmsuYWNjb3VudHMuZGV2JA
Description
useAuth() from @clerk/nextjs frequently returns a new object reference even when session / org / user identity primitives are unchanged. That breaks referential equality for anyone who depends on the whole auth object (Convex’s Clerk bridge, useMemo/useCallback(..., [auth]), context providers that spread auth).
This showed up after upgrading to @clerk/nextjs ^7.9.x (felt ~1–2 weeks after our 2026-10-03…2026-10-06 bump; not present ~1 month earlier on older Clerk).
Steps to reproduce (integration shape):
- Next.js App Router +
@clerk/nextjs+ ConvexConvexProviderWithClerk(orConvexProviderWithAuth) wired with ClerkuseAuth/ a wrapper that returns or spreads the Clerk auth object. - Sign in with an active organization; idle on an authenticated shell (header + sidebar + heavy child such as a data table).
- Watch React Profiler while Clerk performs session/token ticks (no navigation, no org switch).
Expected behavior:
- When
isLoaded,isSignedIn,userId,sessionId,orgId,orgRole(etc.) are unchanged,useAuth()keeps a stable object reference, or Clerk documents stable selectors / field subscriptions so adapters need not depend on a new object every tick. - Transient token-refresh flags (e.g.
isRefreshing) should not force a full auth-object identity change that resets Convex auth or re-renders every subscriber’s subtree without an opt-in.
Actual behavior:
useAuth()often yields a new object with the same identity fields.- If the app (or Convex’s adapter) memoizes on
[auth]/[useAuth]prop identity, Convex auth can transition to loading and re-runfetchAccessToken(https://docs.convex.dev/api/modules/react), re-rendering the authenticated tree (shell + main). - Even with a stable
useAuthfunction prop, spreading the Clerk auth object / reacting toisRefreshingstill updates Convex auth context and multiple Clerk subscribers in one commit. We measured React Profiler commit roots underConvexProviderWithAuthcascading into sidebar, session gate, and a large table (~80ms class work on our pages).
Why consumers cannot only destructure primitives:
- Convex’s official Clerk integration takes a
useAuthhook and builds an adapter from its return value — object identity matters at the bridge, not only in leaf UI. useCallback/useMemowith[auth]rebuild whenever the return reference changes.isRefreshingand similar fields change without changingsessionId/orgId, still producing a new object and notifying dependents.
Ask: Please restore / guarantee stable referential equality on the useAuth() return value when identity fields are unchanged, or ship documented stable selectors for adapter authors.
Related (not a duplicate): #201 (getToken identity per render, closed 2023). This report is about the entire return object / refresh-driven churn with Convex and React 19.
Environment
Packages are hard linked from the content-addressable store to the virtual store.
Content-addressable store is at: /home/carlos/.local/share/pnpm/store/v11
Virtual store is at: ../../../.cache/pnpm/dlx/966750f75ec0f0014a3ee4d556992040/mvkdzm7i-uc0/node_modules/.pacquet
.../mvkdzm7i-uc0 | Progress: resolved 1, reused 0, downloaded 1, added 1, done
dependencies:
+ envinfo 7.21.0
System:
OS: Linux 6.18 Ubuntu 24.04.5 LTS 24.04.5 LTS (Noble Numbat)
CPU: (24) x64 13th Gen Intel(R) Core(TM) i9-13900KF
Memory: 4.79 GB / 15.51 GB
Container: Yes
Shell: 5.9 - /usr/bin/zsh
Binaries:
Node: 24.21.0 - /home/carlos/.local/share/mise/installs/node/lts/bin/node
npm: 11.19.0 - /home/carlos/.local/share/mise/installs/node/lts/bin/npm
pnpm: 12.4.1 - /home/carlos/.local/share/mise/installs/node/lts/bin/pnpm
Browsers:
Chrome: 156.0.8078.47
npmPackages:
@base-ui/react: ^1.9.0 => 1.9.0
@clerk/backend: ~3.26.1 => 3.26.1
@clerk/localizations: ~4.24.0 => 4.24.0
@clerk/nextjs: ^7.9.19 => 7.9.19
@clerk/testing: ^2.2.49 => 2.2.49
@clerk/ui: ~1.41.1 => 1.41.1
@convex-dev/eslint-plugin: ^5.0.0 => 5.0.0
@convex-dev/migrations: ^0.3.7 => 0.3.7
@convex-dev/rate-limiter: ^0.4.2 => 0.4.2
@convex-dev/resend: ^0.2.10 => 0.2.10
@edge-runtime/vm: ^5.0.0 => 5.0.0
@nx/js: 23.3.0 => 23.3.0
@nx/next: 23.3.0 => 23.3.0
@nx/oxlint: 23.3.0 => 23.3.0
@nx/playwright: 23.3.0 => 23.3.0
@nx/vite: 23.3.0 => 23.3.0
@nx/vitest: 23.3.0 => 23.3.0
@nx/workspace: 23.3.0 => 23.3.0
@playwright/test: ^1.64.1 => 1.64.1
@react-pdf/renderer: ^4.9.0 => 4.9.0
@swc-node/register: ^1.12.1 => 1.12.1
@swc/core: ^1.16.20 => 1.16.20
@t3-oss/env-nextjs: ^0.13.11 => 0.13.11
@tailwindcss/postcss: ^4.3.3 => 4.3.3
@tanstack/react-form: ^1.33.5 => 1.33.5
@tanstack/react-query: ^5.104.5 => 5.104.5
@tanstack/react-table: ^9.3.1 => 9.3.1
@tanstack/store: 0.11.2 => 0.11.2
@testing-library/dom: ^10.4.2 => 10.4.2
@testing-library/jest-dom: ^7.0.1 => 7.0.1
@testing-library/react: ^16.3.3 => 16.3.3
@testing-library/user-event: ^14.6.8 => 14.6.8
@types/lodash: ^4.17.27 => 4.17.27
@types/node: ^26.6.5 => 26.6.5
@types/react: 19.3.0 => 19.3.0
@types/react-dom: 19.3.0 => 19.3.0
@vital-link/ui: workspace:* => 0.1.0
@vitejs/plugin-react: ^6.1.2 => 6.1.2
@vitest/coverage-v8: ^5.0.5 => 5.0.5
@vitest/ui: ^5.0.5 => 5.0.5
babel-plugin-react-compiler: ^1.0.0 => 1.0.0
class-variance-authority: ^0.7.1 => 0.7.1
clsx: ^2.1.1 => 2.1.1
cmdk: ^1.1.1 => 1.1.1
concurrently: ^10.0.6 => 10.0.6
convex: ^1.48.0 => 1.48.0
convex-helpers: ^0.1.129 => 0.1.129
convex-test: ^0.0.64 => 0.0.64
date-fns: ^4.4.0 => 4.4.0
date-fns-tz: ^3.2.0 => 3.2.0
dotenv: ^18.0.9 => 18.0.9
extract-zip: 2.0.1 => 2.0.1
fallow: ^3.35.0 => 3.35.0
happy-dom: ^20.14.6 => 20.14.6
husky: ^9.1.7 => 9.1.7
jszip: ^3.10.2 => 3.10.2
lint-staged: ^17.6.0 => 17.6.0
lodash: ^4.18.1 => 4.18.1
lucide-react: ^1.61.0 => 1.61.0
next: 16.4.2 => 16.4.2
next-intl: ^4.14.9 => 4.14.9
next-themes: ^0.4.6 => 0.4.6
nuqs: ^2.10.2 => 2.10.2
nx: 23.3.0 => 23.3.0
oxfmt: ^0.74.0 => 0.74.0
oxlint: ^1.89.0 => 1.89.0
picocolors: ^1.1.1 => 1.1.1
postcss: ^8.5.31 => 8.5.31
qrcode.react: ^4.2.0 => 4.2.0
react: 19.3.1 => 19.3.1
react-day-picker: ^10.0.2 => 10.0.2
react-dom: 19.3.1 => 19.3.1
react-dropzone: ^20.1.4 => 20.1.4
react-email: ^6.11.3 => 6.11.3
react-rnd: ^10.5.3 => 10.5.3
react-use: ^17.6.1 => 17.6.1
resend: ^6.34.1 => 6.34.1
shadcn: ^4.22.1 => 4.22.1
sonner: ^2.0.8 => 2.0.8
svix: ~2.8.0 => 2.8.0
tailwind-merge: ^3.7.0 => 3.7.0
tailwindcss: ^4.3.3 => 4.3.3
tsx: ^4.23.15 => 4.23.15
tw-animate-css: ^1.4.0 => 1.4.0
typescript: ^6.0.3 => 6.0.3
vite: ^8.3.5 => 8.3.5
vitest: ^5.0.5 => 5.0.5
xlsx: ^0.18.5 => 0.18.5
zod: ^4.6.7 => 4.6.7
zustand: ^5.0.16 => 5.0.16
Done in 944ms using pnpm v12.4.1
Activity
luchillo17 commentedon Oct 24, 2026
Forgot to attach the React Devtools profiling file:
profiling-data.10-23-2026.00-56-32.json