From 3ab67adef249af8874b6b654dd759313d3f5ef9b Mon Sep 17 00:00:00 2001 From: Jacek Date: Wed, 17 Jun 2026 19:58:46 -0500 Subject: [PATCH] fix(react): Use globalThis instead of bare global in the browser Clerk loader The browser loader in isomorphicClerk read/wrote a bare `global.Clerk` identifier that only resolved because of the `window.global` polyfill side-effect on the package's main entry. The provider runs through `@clerk/react/internal`, which never imports that polyfill, so after the tsup to tsdown migration split the package into separate chunks a downstream production build could reach the loader with `global` undefined and throw `ReferenceError: global is not defined`. Switch the loader to `globalThis` (always defined; equals `window` in the browser, where clerk-js writes `window.Clerk`) and add a `declare global` augmentation so it type-checks. The polyfill is kept for backward-compat. Fixes #8871 --- .changeset/silly-pumas-cheer.md | 5 +++++ packages/react/src/isomorphicClerk.ts | 20 +++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) create mode 100644 .changeset/silly-pumas-cheer.md diff --git a/.changeset/silly-pumas-cheer.md b/.changeset/silly-pumas-cheer.md new file mode 100644 index 00000000000..042d00a4aa0 --- /dev/null +++ b/.changeset/silly-pumas-cheer.md @@ -0,0 +1,5 @@ +--- +'@clerk/react': patch +--- + +Fix `ReferenceError: global is not defined` thrown from the browser Clerk loader during `` startup in production builds (seen in TanStack Start and React Router apps). The loader now reads the Clerk global via `globalThis` instead of relying on a `window.global` polyfill side-effect, whose load order across bundler chunks was not guaranteed. diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index b38b399005b..5e12b5924af 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -91,13 +91,11 @@ const SDK_METADATA = { environment: process.env.NODE_ENV, }; -export interface Global { - Clerk?: HeadlessBrowserClerk | BrowserClerk; - __internal_ClerkUICtor?: ClerkUIConstructor; +declare global { + var Clerk: HeadlessBrowserClerk | BrowserClerk | undefined; + var __internal_ClerkUICtor: ClerkUIConstructor | undefined; } -declare const global: Global; - type GenericFunction = (...args: TArgs[]) => unknown; type MethodName = { @@ -548,19 +546,19 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { }); } - // Otherwise, set global.Clerk to the bundled ctor or instance + // Otherwise, set globalThis.Clerk to the bundled ctor or instance if (this.options.Clerk && !this.options.__internal_clerkJSUrl) { - global.Clerk = isConstructor(this.options.Clerk) + globalThis.Clerk = isConstructor(this.options.Clerk) ? new this.options.Clerk(this.#publishableKey, { proxyUrl: this.proxyUrl, domain: this.domain }) : this.options.Clerk; } - if (!global.Clerk) { + if (!globalThis.Clerk) { // TODO @nikos: somehow throw if clerk ui failed to load but it was not headless throw new Error('Failed to download latest ClerkJS. Contact support@clerk.com.'); } - return global.Clerk; + return globalThis.Clerk; } private async getClerkUIEntryChunk(): Promise { @@ -587,11 +585,11 @@ export class IsomorphicClerk implements IsomorphicLoadedClerk { nonce: this.options.nonce, }); - if (!global.__internal_ClerkUICtor) { + if (!globalThis.__internal_ClerkUICtor) { throw new Error('Failed to download latest Clerk UI. Contact support@clerk.com.'); } - return global.__internal_ClerkUICtor; + return globalThis.__internal_ClerkUICtor; } return undefined;