Skip to content

Commit 41ac43f

Browse files
authored
feat(testing): Add nextjs cache components test app (#7728)
1 parent 0b4b481 commit 41ac43f

25 files changed

Lines changed: 930 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ jobs:
324324
- test-name: "quickstart"
325325
test-project: "chrome"
326326
next-version: "16"
327+
- test-name: "cache-components"
328+
test-project: "chrome"
329+
next-version: "16"
327330

328331
steps:
329332
- name: Checkout Repo

integration/presets/longRunningApps.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ export const createLongRunningApps = () => {
3636
{ id: 'next.appRouter.withNeedsClientTrust', config: next.appRouter, env: envs.withNeedsClientTrust },
3737
{ id: 'next.appRouter.withSharedUIVariant', config: next.appRouter, env: envs.withSharedUIVariant },
3838

39+
/**
40+
* NextJS apps - cache components
41+
*/
42+
{ id: 'next.cacheComponents', config: next.cacheComponents, env: envs.withEmailCodes },
43+
3944
/**
4045
* Quickstart apps
4146
*/

integration/presets/next.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,23 @@ const appRouterAPWithClerkNextV6 = appRouterQuickstartV6
3636
.setName('next-app-router-ap-clerk-next-v6')
3737
.addDependency('@clerk/nextjs', '6');
3838

39+
const cacheComponents = applicationConfig()
40+
.setName('next-cache-components')
41+
.useTemplate(templates['next-cache-components'])
42+
.setEnvFormatter('public', key => `NEXT_PUBLIC_${key}`)
43+
.addScript('setup', constants.E2E_NPM_FORCE ? 'pnpm install --force' : 'pnpm install')
44+
.addScript('dev', 'pnpm dev')
45+
.addScript('build', 'pnpm build')
46+
.addScript('serve', 'pnpm start')
47+
.addDependency('@clerk/nextjs', constants.E2E_CLERK_JS_VERSION || linkPackage('nextjs'))
48+
.addDependency('@clerk/shared', linkPackage('shared'));
49+
3950
export const next = {
4051
appRouter,
4152
appRouterTurbo,
4253
appRouterQuickstart,
4354
appRouterAPWithClerkNextLatest,
4455
appRouterAPWithClerkNextV6,
4556
appRouterQuickstartV6,
57+
cacheComponents,
4658
} as const;

integration/templates/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const templates = {
55
// If /integration becomes a module in the future, use these helpers:
66
// 'next-app-router': fileURLToPath(new URL('./next-app-router', import.meta.url)),
77
'next-app-router': resolve(__dirname, './next-app-router'),
8+
'next-cache-components': resolve(__dirname, './next-cache-components'),
89
'next-app-router-quickstart': resolve(__dirname, './next-app-router-quickstart'),
910
'next-app-router-quickstart-v6': resolve(__dirname, './next-app-router-quickstart-v6'),
1011
'react-cra': resolve(__dirname, './react-cra'),
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# dependencies
2+
/node_modules
3+
4+
# next.js
5+
/.next/
6+
/out/
7+
8+
# production
9+
/build
10+
11+
# misc
12+
.DS_Store
13+
*.pem
14+
15+
# debug
16+
npm-debug.log*
17+
18+
# local env files
19+
.env*.local
20+
21+
# typescript
22+
*.tsbuildinfo
23+
next-env.d.ts
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Next.js Cache Components Integration Test App
2+
3+
This app tests Clerk's integration with Next.js 16's experimental cache components feature.
4+
5+
## Setup
6+
7+
```bash
8+
pnpm install
9+
pnpm dev
10+
```
11+
12+
## Configuration
13+
14+
The app enables cache components in `next.config.js`:
15+
16+
```js
17+
cacheComponents: true, // Enables PPR and cache components
18+
```
19+
20+
**Important**: ClerkProvider must be wrapped in `<Suspense>` for cache components to work correctly.
21+
22+
## Test Scenarios
23+
24+
### 1. auth() in Server Component (`/auth-server-component`)
25+
26+
Tests basic usage of `auth()` in a React Server Component.
27+
28+
### 2. auth() in Server Action (`/auth-server-action`)
29+
30+
Tests using `auth()` inside a server action triggered by a client component.
31+
32+
### 3. auth() in API Route (`/api/auth-check`)
33+
34+
Tests using `auth()` in a Next.js API route handler.
35+
36+
### 4. "use cache" with auth() - Error Case (`/use-cache-error`)
37+
38+
Tests that calling `auth()` inside a `"use cache"` function produces the expected error.
39+
This is an **invalid pattern** because `auth()` uses dynamic APIs (cookies, headers).
40+
41+
### 5. "use cache" Correct Pattern (`/use-cache-correct`)
42+
43+
Demonstrates the correct way to use `"use cache"` with Clerk:
44+
45+
1. Call `auth()` **outside** the cache function
46+
2. Pass the `userId` **into** the cache function
47+
3. The cache function only contains cacheable operations
48+
49+
### 6. PPR with auth() (`/ppr-auth`)
50+
51+
Tests Partial Pre-Rendering with authenticated content.
52+
Static content is pre-rendered while authenticated content streams in dynamically.
53+
54+
### 7. Protected Route (`/protected`)
55+
56+
Tests middleware-based route protection using `auth.protect()`.
57+
58+
## Expected Behaviors
59+
60+
| Scenario | Expected Result |
61+
| --------------------------------- | ------------------------------------------ |
62+
| auth() in RSC | Works normally |
63+
| auth() in Server Action | Works normally |
64+
| auth() in API Route | Works normally |
65+
| auth() inside "use cache" | Should throw error |
66+
| userId passed to "use cache" | Works correctly |
67+
| PPR + auth() | Dynamic portion streams after static shell |
68+
| Protected route (unauthenticated) | Redirects to sign-in |
69+
70+
## Related PRs
71+
72+
- PR #7119: Initial exploration of cacheComponents support
73+
- PR #7530: Initial exploration of PPR + auth() issues
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
outputFileTracingRoot: '/',
4+
cacheComponents: true,
5+
};
6+
7+
module.exports = nextConfig;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "next-cache-components",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"build": "next build",
7+
"dev": "next dev",
8+
"lint": "next lint",
9+
"start": "next start"
10+
},
11+
"dependencies": {
12+
"@clerk/nextjs": "workspace:*",
13+
"@types/node": "^18.19.33",
14+
"@types/react": "^19.0.0",
15+
"@types/react-dom": "^19.0.0",
16+
"next": "^16.0.0-canary.0",
17+
"react": "19.0.0",
18+
"react-dom": "19.0.0",
19+
"typescript": "^5.7.3"
20+
},
21+
"engines": {
22+
"node": ">=20.9.0"
23+
}
24+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { auth } from '@clerk/nextjs/server';
2+
import { NextResponse } from 'next/server';
3+
4+
export async function GET() {
5+
const { userId, sessionId } = await auth();
6+
7+
return NextResponse.json({
8+
userId: userId ?? null,
9+
sessionId: sessionId ?? null,
10+
isSignedIn: !!userId,
11+
});
12+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use server';
2+
3+
import { auth } from '@clerk/nextjs/server';
4+
5+
export async function checkAuthAction() {
6+
const { userId, sessionId } = await auth();
7+
return { userId, sessionId };
8+
}

0 commit comments

Comments
 (0)