From 0ea3e0d38c88028305e15066b188c8c475b873a1 Mon Sep 17 00:00:00 2001
From: DevCalebR
Date: Thu, 18 Jun 2026 04:53:39 -0400
Subject: [PATCH] Fix localhost Clerk auth rendering
---
app/(auth)/sign-in/[[...sign-in]]/page.tsx | 21 +++++++--------
app/(auth)/sign-up/[[...sign-up]]/page.tsx | 21 +++++++--------
components/auth/clerk-sign-in-card.tsx | 16 ++++++++++++
components/auth/clerk-sign-up-card.tsx | 16 ++++++++++++
lib/clerk-config.ts | 19 ++++++++++++++
tests/clerk-config.test.ts | 30 ++++++++++++++++++++++
tests/public-auth-routing.test.ts | 26 ++++++++++---------
7 files changed, 113 insertions(+), 36 deletions(-)
create mode 100644 components/auth/clerk-sign-in-card.tsx
create mode 100644 components/auth/clerk-sign-up-card.tsx
diff --git a/app/(auth)/sign-in/[[...sign-in]]/page.tsx b/app/(auth)/sign-in/[[...sign-in]]/page.tsx
index 557e85a..28de8eb 100644
--- a/app/(auth)/sign-in/[[...sign-in]]/page.tsx
+++ b/app/(auth)/sign-in/[[...sign-in]]/page.tsx
@@ -1,19 +1,21 @@
import { auth } from '@clerk/nextjs/server';
-import { SignIn } from '@clerk/nextjs';
+import dynamic from 'next/dynamic';
import { redirect } from 'next/navigation';
import { getAdminSession } from '@/lib/admin';
import { getBusinessForOwnerClerkId } from '@/lib/business-access';
import {
- DEFAULT_CLERK_AFTER_AUTH_URL,
- DEFAULT_CLERK_SIGN_IN_URL,
- DEFAULT_CLERK_SIGN_UP_URL,
- hasRequiredValidClerkEnv,
+ canUseClerkClientComponents,
} from '@/lib/clerk-config';
import { resolveSignedInAppDestination } from '@/lib/public-auth-routing';
+const ClerkSignInCard = dynamic(
+ () => import('@/components/auth/clerk-sign-in-card').then((module) => module.ClerkSignInCard),
+ { ssr: false }
+);
+
export default async function SignInPage() {
- if (!hasRequiredValidClerkEnv()) {
+ if (!canUseClerkClientComponents()) {
return (
@@ -60,12 +62,7 @@ export default async function SignInPage() {
-
+
);
diff --git a/app/(auth)/sign-up/[[...sign-up]]/page.tsx b/app/(auth)/sign-up/[[...sign-up]]/page.tsx
index c6f77c0..5b910b6 100644
--- a/app/(auth)/sign-up/[[...sign-up]]/page.tsx
+++ b/app/(auth)/sign-up/[[...sign-up]]/page.tsx
@@ -1,17 +1,19 @@
import { auth } from '@clerk/nextjs/server';
-import { SignUp } from '@clerk/nextjs';
+import dynamic from 'next/dynamic';
import { redirect } from 'next/navigation';
import { getAdminSession } from '@/lib/admin';
import { getBusinessForOwnerClerkId } from '@/lib/business-access';
import {
- DEFAULT_CLERK_AFTER_AUTH_URL,
- DEFAULT_CLERK_SIGN_IN_URL,
- DEFAULT_CLERK_SIGN_UP_URL,
- hasRequiredValidClerkEnv,
+ canUseClerkClientComponents,
} from '@/lib/clerk-config';
import { resolveSignedInAppDestination } from '@/lib/public-auth-routing';
+const ClerkSignUpCard = dynamic(
+ () => import('@/components/auth/clerk-sign-up-card').then((module) => module.ClerkSignUpCard),
+ { ssr: false }
+);
+
function getIntentCopy(intent: string | undefined) {
if (intent === 'pilot') {
return {
@@ -35,7 +37,7 @@ export default async function SignUpPage({
}: {
searchParams?: Record;
}) {
- if (!hasRequiredValidClerkEnv()) {
+ if (!canUseClerkClientComponents()) {
const intent = typeof searchParams?.intent === 'string' ? searchParams.intent : undefined;
const copy = getIntentCopy(intent);
@@ -90,12 +92,7 @@ export default async function SignUpPage({
-
+
);
diff --git a/components/auth/clerk-sign-in-card.tsx b/components/auth/clerk-sign-in-card.tsx
new file mode 100644
index 0000000..83d2bb8
--- /dev/null
+++ b/components/auth/clerk-sign-in-card.tsx
@@ -0,0 +1,16 @@
+'use client';
+
+import { SignIn } from '@clerk/nextjs';
+
+import { DEFAULT_CLERK_AFTER_AUTH_URL, DEFAULT_CLERK_SIGN_IN_URL, DEFAULT_CLERK_SIGN_UP_URL } from '@/lib/clerk-config';
+
+export function ClerkSignInCard() {
+ return (
+
+ );
+}
diff --git a/components/auth/clerk-sign-up-card.tsx b/components/auth/clerk-sign-up-card.tsx
new file mode 100644
index 0000000..9161e68
--- /dev/null
+++ b/components/auth/clerk-sign-up-card.tsx
@@ -0,0 +1,16 @@
+'use client';
+
+import { SignUp } from '@clerk/nextjs';
+
+import { DEFAULT_CLERK_AFTER_AUTH_URL, DEFAULT_CLERK_SIGN_IN_URL, DEFAULT_CLERK_SIGN_UP_URL } from '@/lib/clerk-config';
+
+export function ClerkSignUpCard() {
+ return (
+
+ );
+}
diff --git a/lib/clerk-config.ts b/lib/clerk-config.ts
index 31905cb..49474b8 100644
--- a/lib/clerk-config.ts
+++ b/lib/clerk-config.ts
@@ -50,8 +50,27 @@ export function hasRequiredValidClerkEnv(env: EnvMap = process.env) {
);
}
+export function canUseClerkClientComponents(env: EnvMap = process.env) {
+ const publishableKey = env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY?.trim() ?? '';
+ if (!publishableKey || !isLikelyValidClerkPublishableKey(publishableKey)) {
+ return false;
+ }
+
+ // Localhost cannot use the production Clerk frontend API origin, so prefer the
+ // explicit auth-unavailable fallback instead of mounting broken widgets.
+ if (env.NODE_ENV !== 'production' && publishableKey.startsWith('pk_live_')) {
+ return false;
+ }
+
+ return true;
+}
+
export function resolveClerkPublishableKey(env: EnvMap = process.env) {
const configured = env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY?.trim() ?? '';
+ if (configured && !canUseClerkClientComponents(env)) {
+ return CLERK_PREVIEW_FALLBACK_KEY;
+ }
+
if (configured && isLikelyValidClerkPublishableKey(configured)) {
return configured;
}
diff --git a/tests/clerk-config.test.ts b/tests/clerk-config.test.ts
index 253e697..a80da21 100644
--- a/tests/clerk-config.test.ts
+++ b/tests/clerk-config.test.ts
@@ -2,10 +2,12 @@ import assert from 'node:assert/strict';
import test from 'node:test';
import {
+ canUseClerkClientComponents,
DEFAULT_CLERK_SIGN_IN_URL,
DEFAULT_CLERK_SIGN_UP_URL,
getClerkAuthUrls,
getClerkFrontendApiOrigin,
+ resolveClerkPublishableKey,
} from '../lib/clerk-config.ts';
test('getClerkAuthUrls normalizes env routes to stable base paths for Clerk path routing', () => {
@@ -36,3 +38,31 @@ test('getClerkFrontendApiOrigin returns null for invalid publishable keys', () =
null
);
});
+
+test('canUseClerkClientComponents disables broken localhost live-key widgets during local development', () => {
+ assert.equal(
+ canUseClerkClientComponents({
+ NODE_ENV: 'development',
+ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: 'pk_live_Y2xlcmsuY2FsbGJhY2tjbG9zZXIuY29tJA',
+ }),
+ false
+ );
+
+ assert.equal(
+ canUseClerkClientComponents({
+ NODE_ENV: 'development',
+ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: 'pk_test_Y3VyaW91cy1yaGluby00NS5jbGVyay5hY2NvdW50cy5kZXYk',
+ }),
+ true
+ );
+});
+
+test('resolveClerkPublishableKey falls back to the preview key for local development with live Clerk keys', () => {
+ assert.equal(
+ resolveClerkPublishableKey({
+ NODE_ENV: 'development',
+ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: 'pk_live_Y2xlcmsuY2FsbGJhY2tjbG9zZXIuY29tJA',
+ }),
+ 'pk_test_Y2xlcmsuZXhhbXBsZS5jb20k'
+ );
+});
diff --git a/tests/public-auth-routing.test.ts b/tests/public-auth-routing.test.ts
index 6eac65c..250337a 100644
--- a/tests/public-auth-routing.test.ts
+++ b/tests/public-auth-routing.test.ts
@@ -43,27 +43,29 @@ test('clerk auth surfaces use explicit path routing and fallback redirects', ()
assert.match(layout, /signInFallbackRedirectUrl=\{DEFAULT_CLERK_AFTER_AUTH_URL\}/);
assert.match(layout, /signUpFallbackRedirectUrl=\{DEFAULT_CLERK_AFTER_AUTH_URL\}/);
- assert.match(signInPage, /routing="path"/);
- assert.match(signInPage, /fallbackRedirectUrl=\{DEFAULT_CLERK_AFTER_AUTH_URL\}/);
- assert.match(signInPage, /