diff --git a/.changeset/nasty-weeks-hear.md b/.changeset/nasty-weeks-hear.md new file mode 100644 index 00000000000..c3600c5b78f --- /dev/null +++ b/.changeset/nasty-weeks-hear.md @@ -0,0 +1,5 @@ +--- +'@clerk/nextjs': patch +--- + +Do not run `invalidateCacheAction` on Next.js v15 during `onBeforeSetActive` due to v15's less aggressive router cache. diff --git a/integration/templates/next-app-router/src/app/page.tsx b/integration/templates/next-app-router/src/app/page.tsx index 0151dfe307c..d6be346c4f9 100644 --- a/integration/templates/next-app-router/src/app/page.tsx +++ b/integration/templates/next-app-router/src/app/page.tsx @@ -17,6 +17,7 @@ export default function Home() { diff --git a/integration/templates/next-app-router/src/app/protected/page.tsx b/integration/templates/next-app-router/src/app/protected/page.tsx index 22d3dbfe2f8..0e50a12882d 100644 --- a/integration/templates/next-app-router/src/app/protected/page.tsx +++ b/integration/templates/next-app-router/src/app/protected/page.tsx @@ -1,3 +1,10 @@ +import Link from 'next/link'; + export default function Page() { - return
Protected
; + return ( + <> +
Protected
+ Home + + ); } diff --git a/integration/tests/sign-out-smoke.test.ts b/integration/tests/sign-out-smoke.test.ts index 129947774c7..2ffd558e3a3 100644 --- a/integration/tests/sign-out-smoke.test.ts +++ b/integration/tests/sign-out-smoke.test.ts @@ -68,6 +68,24 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign out const client_id_after_sign_out = await u.page.locator('p[data-clerk-id]').innerHTML(); expect(client_id).toEqual(client_id_after_sign_out); }); + + test('Protected routes do not persist after sign out', async ({ page, context }) => { + const u = createTestUtils({ app, page, context }); + + await u.po.signIn.goTo(); + await u.po.signIn.waitForMounted(); + await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password }); + await u.po.expect.toBeSignedIn(); + + await u.page.getByRole('link', { name: 'Protected', exact: true }).click(); + await u.page.getByTestId('protected').waitFor(); + await u.page.getByRole('link', { name: 'Home' }).click(); + await u.page.getByRole('button', { name: 'Open user button' }).click(); + + await u.page.getByRole('menuitem', { name: 'Sign out' }).click(); + await u.page.getByRole('link', { name: 'Protected', exact: true }).click(); + await u.page.waitForURL(/sign-in\?redirect_url/); + }); }); testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes_destroy_client] })( diff --git a/packages/nextjs/src/app-router/client/ClerkProvider.tsx b/packages/nextjs/src/app-router/client/ClerkProvider.tsx index b9a9cac154d..57ef0c4234f 100644 --- a/packages/nextjs/src/app-router/client/ClerkProvider.tsx +++ b/packages/nextjs/src/app-router/client/ClerkProvider.tsx @@ -75,16 +75,21 @@ const NextClientClerkProvider = (props: NextClerkProviderProps) => { * For more information on cache invalidation, see: * https://nextjs.org/docs/app/building-your-application/caching#invalidation-1 */ - return new Promise(res => { - window.__clerk_internal_invalidateCachePromise = res; + return new Promise(resolve => { + window.__clerk_internal_invalidateCachePromise = resolve; // NOTE: the following code will allow `useReverification()` to work properly when `handlerReverification` is called inside `startTransition` - if (window.next?.version && typeof window.next.version === 'string' && window.next.version.startsWith('13')) { - startTransition(() => { - router.refresh(); - }); - } else { - void invalidateCacheAction().then(() => res()); + if (window.next?.version && typeof window.next.version === 'string') { + if (window.next.version.startsWith('13')) { + startTransition(() => { + router.refresh(); + }); + } else if (window.next.version.startsWith('14')) { + void invalidateCacheAction().then(resolve); + } else { + // Next.js v15 and above have a less aggressive router cache that doesn't need to be invalidated. + resolve(); + } } }); };