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() {
-
Page Protected
+ Protected
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();
+ }
}
});
};