From b8de41928be1da9220a9c0ae52eb37bb2db58fb9 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 23 Sep 2026 08:55:43 -0700 Subject: [PATCH 1/3] test(clerk-js): cover mounting signed-in components during setActive navigation --- .../clerk-js/src/core/__tests__/clerk.test.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/clerk-js/src/core/__tests__/clerk.test.ts b/packages/clerk-js/src/core/__tests__/clerk.test.ts index 1daf950ac80..1dc4a3b3879 100644 --- a/packages/clerk-js/src/core/__tests__/clerk.test.ts +++ b/packages/clerk-js/src/core/__tests__/clerk.test.ts @@ -253,6 +253,36 @@ describe('Clerk singleton', () => { expect(mockSession.touch).toHaveBeenCalledWith({ intent: 'select_session' }); }); + it.each([ + ['OAuthConsent', 'mountOAuthConsent'], + ['UserProfile', 'mountUserProfile'], + ] as const)('mounts %s when mounted while navigating to redirectUrl', async (component, mountMethod) => { + mockSession.__internal_touch.mockReturnValue(Promise.resolve()); + mockClientFetch.mockReturnValue( + Promise.resolve({ signedInSessions: [mockSession], isEligibleForTouch: () => false }), + ); + const mountComponent = vi.fn(); + const mockClerkUICtor = vi.fn(function () { + return { ensureMounted: () => Promise.resolve({ mountComponent }) }; + }); + + const sut = new Clerk(productionPublishableKey); + await sut.load({ ui: { ClerkUI: mockClerkUICtor as any } }); + const node = document.createElement('div'); + sut.navigate = vi.fn(async () => { + sut[mountMethod](node); + }); + + await sut.setActive({ + session: mockSession as any as ActiveSessionResource, + redirectUrl: '/oauth-consent', + }); + + await waitFor(() => { + expect(mountComponent).toHaveBeenCalledWith(expect.objectContaining({ name: component, node })); + }); + }); + describe('with `touchSession` set to false', () => { it('calls session.touch by default outside of focus window event', async () => { mockSession.touch.mockReturnValue(Promise.resolve()); From 3cc9f47459fe658bb6907920dba8856f8f23aa08 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 23 Sep 2026 08:55:44 -0700 Subject: [PATCH 2/3] fix(clerk-js): mount signed-in components during setActive's transitive state --- .changeset/oauth-consent-mount-after-set-active.md | 6 ++++++ packages/shared/src/internal/clerk-js/componentGuards.ts | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .changeset/oauth-consent-mount-after-set-active.md diff --git a/.changeset/oauth-consent-mount-after-set-active.md b/.changeset/oauth-consent-mount-after-set-active.md new file mode 100644 index 00000000000..ad0fab5b11f --- /dev/null +++ b/.changeset/oauth-consent-mount-after-set-active.md @@ -0,0 +1,6 @@ +--- +'@clerk/clerk-js': patch +'@clerk/shared': patch +--- + +Fix ``, ``, and other signed-in components rendering a blank page when they mount during a client-side navigation triggered by `setActive()`, such as the redirect after completing `` or ``. diff --git a/packages/shared/src/internal/clerk-js/componentGuards.ts b/packages/shared/src/internal/clerk-js/componentGuards.ts index bb81268a6b9..3919538b11e 100644 --- a/packages/shared/src/internal/clerk-js/componentGuards.ts +++ b/packages/shared/src/internal/clerk-js/componentGuards.ts @@ -11,11 +11,11 @@ export const isSignedInAndSingleSessionModeEnabled: ComponentGuard = (clerk, env }; export const noUserExists: ComponentGuard = clerk => { - return !clerk.user; + return clerk.user === null; }; export const noOrganizationExists: ComponentGuard = clerk => { - return !clerk.organization; + return clerk.organization === null; }; export const disabledOrganizationsFeature: ComponentGuard = (_, environment) => { From 9bda8252e0b181c7fd2a6154b9d2eafa1fddbd53 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 23 Sep 2026 09:06:43 -0700 Subject: [PATCH 3/3] fix(clerk-js): scope setActive mount fix to mountOAuthConsent --- .changeset/oauth-consent-mount-after-set-active.md | 3 +-- packages/clerk-js/src/core/__tests__/clerk.test.ts | 12 ++++-------- packages/clerk-js/src/core/clerk.ts | 2 +- .../shared/src/internal/clerk-js/componentGuards.ts | 4 ++-- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/.changeset/oauth-consent-mount-after-set-active.md b/.changeset/oauth-consent-mount-after-set-active.md index ad0fab5b11f..3ff86da44ec 100644 --- a/.changeset/oauth-consent-mount-after-set-active.md +++ b/.changeset/oauth-consent-mount-after-set-active.md @@ -1,6 +1,5 @@ --- '@clerk/clerk-js': patch -'@clerk/shared': patch --- -Fix ``, ``, and other signed-in components rendering a blank page when they mount during a client-side navigation triggered by `setActive()`, such as the redirect after completing `` or ``. +Fix `` rendering a blank page when reached through the redirect after completing `` or ``. diff --git a/packages/clerk-js/src/core/__tests__/clerk.test.ts b/packages/clerk-js/src/core/__tests__/clerk.test.ts index 1dc4a3b3879..18451d7bc03 100644 --- a/packages/clerk-js/src/core/__tests__/clerk.test.ts +++ b/packages/clerk-js/src/core/__tests__/clerk.test.ts @@ -253,11 +253,7 @@ describe('Clerk singleton', () => { expect(mockSession.touch).toHaveBeenCalledWith({ intent: 'select_session' }); }); - it.each([ - ['OAuthConsent', 'mountOAuthConsent'], - ['UserProfile', 'mountUserProfile'], - ] as const)('mounts %s when mounted while navigating to redirectUrl', async (component, mountMethod) => { - mockSession.__internal_touch.mockReturnValue(Promise.resolve()); + it('mounts OAuthConsent when mounted while navigating to redirectUrl', async () => { mockClientFetch.mockReturnValue( Promise.resolve({ signedInSessions: [mockSession], isEligibleForTouch: () => false }), ); @@ -267,10 +263,10 @@ describe('Clerk singleton', () => { }); const sut = new Clerk(productionPublishableKey); - await sut.load({ ui: { ClerkUI: mockClerkUICtor as any } }); + await sut.load({ ui: { ClerkUI: mockClerkUICtor } }); const node = document.createElement('div'); sut.navigate = vi.fn(async () => { - sut[mountMethod](node); + sut.mountOAuthConsent(node); }); await sut.setActive({ @@ -279,7 +275,7 @@ describe('Clerk singleton', () => { }); await waitFor(() => { - expect(mountComponent).toHaveBeenCalledWith(expect.objectContaining({ name: component, node })); + expect(mountComponent).toHaveBeenCalledWith(expect.objectContaining({ name: 'OAuthConsent', node })); }); }); diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 6b719fefaf6..aa2cb555cd7 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1512,7 +1512,7 @@ export class Clerk implements ClerkInterface { }; public mountOAuthConsent = (node: HTMLDivElement, props?: __internal_OAuthConsentProps) => { - if (noUserExists(this)) { + if (this.user === null) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderOAuthConsentComponentWhenUserDoesNotExist, { code: CANNOT_RENDER_USER_MISSING_ERROR_CODE, diff --git a/packages/shared/src/internal/clerk-js/componentGuards.ts b/packages/shared/src/internal/clerk-js/componentGuards.ts index 3919538b11e..bb81268a6b9 100644 --- a/packages/shared/src/internal/clerk-js/componentGuards.ts +++ b/packages/shared/src/internal/clerk-js/componentGuards.ts @@ -11,11 +11,11 @@ export const isSignedInAndSingleSessionModeEnabled: ComponentGuard = (clerk, env }; export const noUserExists: ComponentGuard = clerk => { - return clerk.user === null; + return !clerk.user; }; export const noOrganizationExists: ComponentGuard = clerk => { - return clerk.organization === null; + return !clerk.organization; }; export const disabledOrganizationsFeature: ComponentGuard = (_, environment) => {