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; }