Skip to content

Commit b6431b1

Browse files
committed
wip
1 parent 6d06be8 commit b6431b1

File tree

1 file changed

+41
-37
lines changed

1 file changed

+41
-37
lines changed

packages/nextjs/src/utils/clerk-js-script.tsx

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useClerk } from '@clerk/clerk-react';
22
import { buildClerkJsScriptAttributes, clerkJsScriptUrl } from '@clerk/clerk-react/internal';
3+
import Head from 'next/head';
34
import NextScript from 'next/script';
45
import React, { useCallback, useEffect, useRef, useState } from 'react';
56

@@ -149,17 +150,14 @@ function useClerkJSLoadingState() {
149150
/**
150151
* Enhanced ClerkJS Script component with bulletproof load detection.
151152
*
152-
* This component renders TWO script tags:
153-
* 1. A render-blocking inline script that sets up the coordinator globally
154-
* 2. The actual ClerkJS script tag (which the coordinator will manage)
155-
*
156-
* The coordinator uses comprehensive DOM interception to catch scripts regardless
157-
* of where they're placed (head, body, or injected dynamically).
153+
* This component ensures the blocking coordinator is loaded in the document head
154+
* before any ClerkJS scripts, regardless of the router type.
158155
*/
159156
function ClerkJSScript(props: ClerkJSScriptProps) {
160157
const { publishableKey, clerkJSUrl, clerkJSVersion, clerkJSVariant, nonce } = useClerkNextOptions();
161158
const { domain, proxyUrl } = useClerk();
162159
const scriptRef = useRef<HTMLScriptElement>(null);
160+
const coordinatorInjected = useRef(false);
163161

164162
/**
165163
* If no publishable key, avoid appending invalid scripts in the DOM.
@@ -179,6 +177,31 @@ function ClerkJSScript(props: ClerkJSScriptProps) {
179177
};
180178
const scriptUrl = clerkJsScriptUrl(options);
181179

180+
// Inject coordinator script into head manually to ensure it's there first
181+
useEffect(() => {
182+
if (typeof window === 'undefined' || coordinatorInjected.current) return;
183+
184+
// Check if coordinator already exists
185+
if ((window as any).__clerkJSBlockingCoordinator) {
186+
coordinatorInjected.current = true;
187+
return;
188+
}
189+
190+
// Create and inject coordinator script into head
191+
const coordinatorScript = document.createElement('script');
192+
coordinatorScript.id = 'clerk-blocking-coordinator';
193+
coordinatorScript.innerHTML = getBlockingCoordinatorScript();
194+
195+
// Insert at the beginning of head to ensure it runs first
196+
if (document.head.firstChild) {
197+
document.head.insertBefore(coordinatorScript, document.head.firstChild);
198+
} else {
199+
document.head.appendChild(coordinatorScript);
200+
}
201+
202+
coordinatorInjected.current = true;
203+
}, []);
204+
182205
// Handle state changes from the blocking coordinator
183206
const handleLoad = useCallback(() => {
184207
props.onLoad?.();
@@ -210,17 +233,9 @@ function ClerkJSScript(props: ClerkJSScriptProps) {
210233
const scriptAttributes = buildClerkJsScriptAttributes(options);
211234

212235
if (props.router === 'app') {
213-
// For App Router, use regular script tags
214-
// The coordinator will catch these regardless of placement
236+
// For App Router, use Next.js Head component to ensure script goes to head
215237
return (
216-
<>
217-
{/* Blocking coordinator script - MUST run first */}
218-
<script
219-
dangerouslySetInnerHTML={{ __html: getBlockingCoordinatorScript() }}
220-
// No async/defer - this must block to set up coordination
221-
/>
222-
223-
{/* Actual ClerkJS script - managed by the coordinator */}
238+
<Head>
224239
<script
225240
ref={scriptRef}
226241
src={scriptUrl}
@@ -229,31 +244,20 @@ function ClerkJSScript(props: ClerkJSScriptProps) {
229244
crossOrigin='anonymous'
230245
{...scriptAttributes}
231246
/>
232-
</>
247+
</Head>
233248
);
234249
} else {
235250
// For Pages Router, use Next.js Script components with beforeInteractive
236-
// This ensures both scripts are placed in head and execute early
237251
return (
238-
<>
239-
{/* Blocking coordinator script - MUST run first and block */}
240-
<NextScript
241-
id='clerk-blocking-coordinator'
242-
strategy='beforeInteractive'
243-
dangerouslySetInnerHTML={{ __html: getBlockingCoordinatorScript() }}
244-
/>
245-
246-
{/* Actual ClerkJS script - managed by the coordinator */}
247-
<NextScript
248-
src={scriptUrl}
249-
data-clerk-js-script='true'
250-
async
251-
defer={false}
252-
crossOrigin='anonymous'
253-
strategy='beforeInteractive'
254-
{...scriptAttributes}
255-
/>
256-
</>
252+
<NextScript
253+
src={scriptUrl}
254+
data-clerk-js-script='true'
255+
async
256+
defer={false}
257+
crossOrigin='anonymous'
258+
strategy='beforeInteractive'
259+
{...scriptAttributes}
260+
/>
257261
);
258262
}
259263
}

0 commit comments

Comments
 (0)