diff --git a/.changeset/brown-kids-camp.md b/.changeset/brown-kids-camp.md
new file mode 100644
index 00000000000..8b8248b3152
--- /dev/null
+++ b/.changeset/brown-kids-camp.md
@@ -0,0 +1,5 @@
+---
+'@clerk/nextjs': patch
+---
+
+Handle `dynamicIO` errors when request apis are accessed on prerender. This fixes issues with `ppr: true, dynamicIO: true` when using ``.
diff --git a/packages/nextjs/src/app-router/server/ClerkProvider.tsx b/packages/nextjs/src/app-router/server/ClerkProvider.tsx
index 69f3693a568..badcbd19519 100644
--- a/packages/nextjs/src/app-router/server/ClerkProvider.tsx
+++ b/packages/nextjs/src/app-router/server/ClerkProvider.tsx
@@ -1,4 +1,3 @@
-import type { AuthObject } from '@clerk/backend';
import type { InitialState, Without } from '@clerk/types';
import { headers } from 'next/headers';
import React from 'react';
@@ -27,21 +26,33 @@ export async function ClerkProvider(
props: Without,
) {
const { children, dynamic, ...rest } = props;
- let statePromise: Promise = Promise.resolve(null);
- let nonce = Promise.resolve('');
- if (dynamic) {
+ async function generateStatePromise() {
+ if (!dynamic) {
+ return Promise.resolve(null);
+ }
+ if (isNext13) {
+ /**
+ * For some reason, Next 13 requires that functions which call `headers()` are awaited where they are invoked.
+ * Without the await here, Next will throw a DynamicServerError during build.
+ */
+ return Promise.resolve(await getDynamicClerkState());
+ }
+ return getDynamicClerkState();
+ }
+
+ async function generateNonce() {
+ if (!dynamic) {
+ return Promise.resolve('');
+ }
if (isNext13) {
/**
* For some reason, Next 13 requires that functions which call `headers()` are awaited where they are invoked.
* Without the await here, Next will throw a DynamicServerError during build.
*/
- statePromise = Promise.resolve(await getDynamicClerkState());
- nonce = Promise.resolve(await getNonceFromCSPHeader());
- } else {
- statePromise = getDynamicClerkState();
- nonce = getNonceFromCSPHeader();
+ return Promise.resolve(await getNonceFromCSPHeader());
}
+ return getNonceFromCSPHeader();
}
const propsWithEnvs = mergeNextClerkPropsWithEnv({
@@ -51,8 +62,8 @@ export async function ClerkProvider(
let output = (
{children}
@@ -75,8 +86,8 @@ export async function ClerkProvider(
__internal_claimKeylessApplicationUrl: newOrReadKeys.claimUrl,
__internal_copyInstanceKeysUrl: newOrReadKeys.apiKeysUrl,
})}
- nonce={await nonce}
- initialState={await statePromise}
+ nonce={await generateNonce()}
+ initialState={await generateStatePromise()}
>
{children}
@@ -88,7 +99,7 @@ export async function ClerkProvider(
if (dynamic) {
return (
// TODO: fix types so AuthObject is compatible with InitialState
- }>
+ }>
{output}
);
diff --git a/packages/nextjs/src/app-router/server/utils.ts b/packages/nextjs/src/app-router/server/utils.ts
index 7e8c220155f..490166d3027 100644
--- a/packages/nextjs/src/app-router/server/utils.ts
+++ b/packages/nextjs/src/app-router/server/utils.ts
@@ -19,7 +19,7 @@ export const isPrerenderingBailout = (e: unknown) => {
return routeRegex.test(message) || dynamicServerUsage || bailOutPrerendering;
};
-export async function buildRequestLike() {
+export async function buildRequestLike(): Promise {
try {
// Dynamically import next/headers, otherwise Next12 apps will break
// @ts-expect-error: Cannot find module 'next/headers' or its corresponding type declarations.ts(2307)
@@ -67,7 +67,7 @@ export function getScriptNonceFromHeader(cspHeaderValue: string): string | undef
// Grab the nonce by trimming the 'nonce-' prefix.
?.slice(7, -1);
- // If we could't find the nonce, then we're done.
+ // If we couldn't find the nonce, then we're done.
if (!nonce) {
return;
}