From 80bba3c2d5b8e8c5dde76505455bf3304d632307 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Sun, 31 Aug 2025 20:25:34 +0300 Subject: [PATCH 1/2] feat(nextjs): Support static export --- packages/nextjs/package.json | 8 +++++++- packages/nextjs/src/static.tsx | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 packages/nextjs/src/static.tsx diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 45c60e0f82f..685ffa2ceda 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -54,6 +54,11 @@ "types": "./dist/types/experimental.d.ts", "import": "./dist/esm/experimental.js", "require": "./dist/cjs/experimental.js" + }, + "./static": { + "types": "./dist/types/static.d.ts", + "import": "./dist/esm/static.js", + "require": "./dist/cjs/static.js" } }, "types": "./dist/types/index.d.ts", @@ -61,7 +66,8 @@ "dist", "server", "errors", - "webhooks" + "webhooks", + "static" ], "scripts": { "build": "pnpm clean && tsup", diff --git a/packages/nextjs/src/static.tsx b/packages/nextjs/src/static.tsx new file mode 100644 index 00000000000..a76d2e6e8f8 --- /dev/null +++ b/packages/nextjs/src/static.tsx @@ -0,0 +1,21 @@ +'use client'; + +import type { ClerkProviderProps } from '@clerk/clerk-react'; +import { ClerkProvider as ReactClerkProvider } from '@clerk/clerk-react'; +import type { Without } from '@clerk/types'; +import React from 'react'; + +import { mergeNextClerkPropsWithEnv } from './utils/mergeNextClerkPropsWithEnv'; + +type NextClerkProviderProps = Without & { + /** + * Used to override the default NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY env variable if needed. + * This is optional for NextJS as the ClerkProvider will automatically use the NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY env variable if it exists. + */ + publishableKey?: string; +}; + +export const ClerkProvider = (props: NextClerkProviderProps) => { + const mergedProps = mergeNextClerkPropsWithEnv(props); + return {props.children}; +}; From 001748855caf5106987b6cc5f6c1af0c350dffe5 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Sun, 31 Aug 2025 20:46:33 +0300 Subject: [PATCH 2/2] improve routing --- packages/nextjs/src/static.tsx | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/nextjs/src/static.tsx b/packages/nextjs/src/static.tsx index a76d2e6e8f8..ffac95560ff 100644 --- a/packages/nextjs/src/static.tsx +++ b/packages/nextjs/src/static.tsx @@ -5,9 +5,11 @@ import { ClerkProvider as ReactClerkProvider } from '@clerk/clerk-react'; import type { Without } from '@clerk/types'; import React from 'react'; +import { useAwaitablePush } from './app-router/client/useAwaitablePush'; +import { useAwaitableReplace } from './app-router/client/useAwaitableReplace'; import { mergeNextClerkPropsWithEnv } from './utils/mergeNextClerkPropsWithEnv'; -type NextClerkProviderProps = Without & { +type NextStaticClerkProviderProps = Without & { /** * Used to override the default NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY env variable if needed. * This is optional for NextJS as the ClerkProvider will automatically use the NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY env variable if it exists. @@ -15,7 +17,27 @@ type NextClerkProviderProps = Without & { publishableKey?: string; }; -export const ClerkProvider = (props: NextClerkProviderProps) => { - const mergedProps = mergeNextClerkPropsWithEnv(props); +// StaticClerkProvider is a light wrapper around the `` exported from `@clerk/clerk-react` +// It is meant to be used only for developers that want to static export their Next.js app (https://nextjs.org/docs/app/guides/static-exports). +// This opts out of keyless, the `dynamic` prop (aka SSR) and route revalidation on auth state updates (since RSCs and middleware do not exist in the context of static export). + +/** + * The ClerkProvider exported from `/static` should only be used for static exports only. + * Using this provider means that you opt out of authorization checks on the server. + * @param props + * @returns + */ +const StaticClerkProvider = (props: NextStaticClerkProviderProps) => { + const push = useAwaitablePush(); + const replace = useAwaitableReplace(); + const mergedProps = mergeNextClerkPropsWithEnv({ + ...props, + // @ts-expect-error Error because of the stricter types of internal `push` + routerPush: push, + // @ts-expect-error Error because of the stricter types of internal `replace` + routerReplace: replace, + }); return {props.children}; }; + +export { StaticClerkProvider as ClerkProvider };