Skip to content

Commit 7b53832

Browse files
committed
replace ClerkJSScript with ClerkScripts to inject clerk/clerk-js and clerk/ui in nextjs apps
1 parent cc2818f commit 7b53832

File tree

7 files changed

+102
-72
lines changed

7 files changed

+102
-72
lines changed

packages/nextjs/src/app-router/client/ClerkProvider.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import React, { useEffect, useTransition } from 'react';
1010
import { useSafeLayoutEffect } from '../../client-boundary/hooks/useSafeLayoutEffect';
1111
import { ClerkNextOptionsProvider, useClerkNextOptions } from '../../client-boundary/NextOptionsContext';
1212
import type { NextClerkProviderProps } from '../../types';
13-
import { ClerkJSScript } from '../../utils/clerk-js-script';
13+
import { ClerkScripts } from '../../utils/clerk-script';
1414
import { canUseKeyless } from '../../utils/feature-flags';
1515
import { mergeNextClerkPropsWithEnv } from '../../utils/mergeNextClerkPropsWithEnv';
1616
import { RouterTelemetry } from '../../utils/router-telemetry';
@@ -130,7 +130,7 @@ const NextClientClerkProvider = (props: NextClerkProviderProps) => {
130130
<ClerkNextOptionsProvider options={mergedProps}>
131131
<ReactClerkProvider {...mergedProps}>
132132
<RouterTelemetry />
133-
<ClerkJSScript router='app' />
133+
<ClerkScripts router='app' />
134134
{children}
135135
</ReactClerkProvider>
136136
</ClerkNextOptionsProvider>

packages/nextjs/src/pages/ClerkProvider.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import React from 'react';
77
import { useSafeLayoutEffect } from '../client-boundary/hooks/useSafeLayoutEffect';
88
import { ClerkNextOptionsProvider } from '../client-boundary/NextOptionsContext';
99
import type { NextClerkProviderProps } from '../types';
10-
import { ClerkJSScript } from '../utils/clerk-js-script';
10+
import { ClerkScripts } from '../utils/clerk-script';
1111
import { invalidateNextRouterCache } from '../utils/invalidateNextRouterCache';
1212
import { mergeNextClerkPropsWithEnv } from '../utils/mergeNextClerkPropsWithEnv';
1313
import { removeBasePath } from '../utils/removeBasePath';
@@ -55,7 +55,7 @@ export function ClerkProvider({ children, ...props }: NextClerkProviderProps): J
5555
initialState={initialState}
5656
>
5757
<RouterTelemetry />
58-
<ClerkJSScript router='pages' />
58+
<ClerkScripts router='pages' />
5959
{children}
6060
</ReactClerkProvider>
6161
</ClerkNextOptionsProvider>

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

Lines changed: 0 additions & 56 deletions
This file was deleted.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { useClerk } from '@clerk/react';
2+
import {
3+
buildClerkJsScriptAttributes,
4+
buildClerkUiScriptAttributes,
5+
clerkJsScriptUrl,
6+
clerkUiScriptUrl,
7+
} from '@clerk/react/internal';
8+
import NextScript from 'next/script';
9+
import React from 'react';
10+
11+
import { useClerkNextOptions } from '../client-boundary/NextOptionsContext';
12+
13+
type ClerkScriptProps = {
14+
scriptUrl: string;
15+
attributes: Record<string, string>;
16+
dataAttribute: string;
17+
router: 'app' | 'pages';
18+
};
19+
20+
function ClerkScript(props: ClerkScriptProps) {
21+
const { scriptUrl, attributes, dataAttribute, router } = props;
22+
23+
/**
24+
* Notes:
25+
* `next/script` in 13.x.x when used with App Router will fail to pass any of our `data-*` attributes, resulting in errors
26+
* Nextjs App Router will automatically move inline scripts inside `<head/>`
27+
* Using the `nextjs/script` for App Router with the `beforeInteractive` strategy will throw an error because our custom script will be mounted outside the `html` tag.
28+
*/
29+
const Script = router === 'app' ? 'script' : NextScript;
30+
31+
return (
32+
<Script
33+
src={scriptUrl}
34+
{...{ [dataAttribute]: true }}
35+
async
36+
// `nextjs/script` will add defer by default and does not get removed when async is true
37+
defer={router === 'pages' ? false : undefined}
38+
crossOrigin='anonymous'
39+
strategy={router === 'pages' ? 'beforeInteractive' : undefined}
40+
{...attributes}
41+
/>
42+
);
43+
}
44+
45+
export function ClerkScripts({ router }: { router: ClerkScriptProps['router'] }) {
46+
const { publishableKey, clerkJSUrl, clerkJSVersion, clerkJSVariant, nonce, clerkUiUrl } = useClerkNextOptions();
47+
const { domain, proxyUrl } = useClerk();
48+
49+
if (!publishableKey) {
50+
return null;
51+
}
52+
53+
const opts = {
54+
publishableKey,
55+
clerkJSUrl,
56+
clerkJSVersion,
57+
clerkJSVariant,
58+
nonce,
59+
domain,
60+
proxyUrl,
61+
clerkUiUrl,
62+
};
63+
64+
return (
65+
<>
66+
<ClerkScript
67+
scriptUrl={clerkJsScriptUrl(opts)}
68+
attributes={buildClerkJsScriptAttributes(opts)}
69+
dataAttribute='data-clerk-js-script'
70+
router={router}
71+
/>
72+
<ClerkScript
73+
scriptUrl={clerkUiScriptUrl(opts)}
74+
attributes={buildClerkUiScriptAttributes(opts)}
75+
dataAttribute='data-clerk-ui-script'
76+
router={router}
77+
/>
78+
</>
79+
);
80+
}

packages/react/src/internal.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ export { useDerivedAuth } from './hooks/useAuth';
66
export {
77
clerkJsScriptUrl,
88
buildClerkJsScriptAttributes,
9+
clerkUiScriptUrl,
10+
buildClerkUiScriptAttributes,
911
setClerkJsLoadingErrorPackageName,
1012
} from '@clerk/shared/loadClerkJsScript';

packages/react/src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ export type IsomorphicClerkOptions = Without<ClerkOptions, 'isSatellite'> & {
3636
* The npm version for `@clerk/clerk-js`.
3737
*/
3838
clerkJSVersion?: string;
39+
/**
40+
* The URL that `@clerk/ui` should be hot-loaded from.
41+
*/
42+
clerkUiUrl?: string;
3943
/**
4044
* The Clerk Publishable Key for your instance. This can be found on the [API keys](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard.
4145
*/

packages/shared/src/loadClerkJsScript.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import { buildErrorThrower, ClerkRuntimeError } from './error';
22
import { createDevOrStagingUrlCache, parsePublishableKey } from './keys';
33
import { loadScript } from './loadScript';
44
import { isValidProxyUrl, proxyUrlToAbsoluteURL } from './proxy';
5-
import type { ClerkOptions, SDKMetadata, Without } from './types';
5+
import type { SDKMetadata } from './types';
66
import { addClerkPrefix } from './url';
77
import { versionSelector } from './versionSelector';
88

99
const { isDevOrStagingUrl } = createDevOrStagingUrlCache();
1010

1111
const errorThrower = buildErrorThrower({ packageName: '@clerk/shared' });
1212

13-
export type LoadClerkJsScriptOptions = Without<ClerkOptions, 'isSatellite'> & {
13+
export type LoadClerkJsScriptOptions = {
1414
publishableKey: string;
1515
clerkJSUrl?: string;
1616
clerkJSVariant?: 'headless' | '';
@@ -27,7 +27,7 @@ export type LoadClerkJsScriptOptions = Without<ClerkOptions, 'isSatellite'> & {
2727
scriptLoadTimeout?: number;
2828
};
2929

30-
export type LoadClerkUiScriptOptions = Without<ClerkOptions, 'isSatellite'> & {
30+
export type LoadClerkUiScriptOptions = {
3131
publishableKey: string;
3232
clerkUiUrl?: string;
3333
proxyUrl?: string;
@@ -104,7 +104,7 @@ export const loadClerkJsScript = async (opts?: LoadClerkJsScriptOptions): Promis
104104
async: true,
105105
crossOrigin: 'anonymous',
106106
nonce: opts.nonce,
107-
beforeLoad: applyClerkJsScriptAttributes(opts),
107+
beforeLoad: applyAttributesToScript(buildClerkJsScriptAttributes(opts)),
108108
}).catch(error => {
109109
throw rejectWith(error);
110110
});
@@ -113,8 +113,6 @@ export const loadClerkJsScript = async (opts?: LoadClerkJsScriptOptions): Promis
113113
};
114114

115115
export const loadClerkUiScript = async (opts?: LoadClerkUiScriptOptions): Promise<HTMLScriptElement | null> => {
116-
console.log('here?');
117-
118116
const timeout = opts?.scriptLoadTimeout ?? 15000;
119117
const rejectWith = (error?: Error) =>
120118
new ClerkRuntimeError('Failed to load Clerk UI' + (error?.message ? `, ${error.message}` : ''), {
@@ -142,8 +140,8 @@ export const loadClerkUiScript = async (opts?: LoadClerkUiScriptOptions): Promis
142140
async: true,
143141
crossOrigin: 'anonymous',
144142
nonce: opts.nonce,
143+
beforeLoad: applyAttributesToScript(buildClerkUiScriptAttributes(opts)),
145144
}).catch(error => {
146-
console.log('nikos 2', error);
147145
throw rejectWith(error);
148146
});
149147

@@ -167,13 +165,11 @@ export const clerkUiScriptUrl = (opts: LoadClerkUiScriptOptions) => {
167165
const { clerkUiUrl, proxyUrl, domain, publishableKey } = opts;
168166

169167
if (clerkUiUrl) {
170-
console.log('got clerk ui url', clerkUiUrl);
171-
172168
return clerkUiUrl;
173169
}
174170

175171
const scriptHost = buildScriptHost({ publishableKey, proxyUrl, domain });
176-
// TODO @nikos add version selector
172+
// TODO: add version selector for clerk/ui similar to clerk-js
177173
return `https://${scriptHost}/npm/@clerk/ui@latest/dist/ui.browser.js`;
178174
};
179175

@@ -199,8 +195,12 @@ export const buildClerkJsScriptAttributes = (options: LoadClerkJsScriptOptions)
199195
return obj;
200196
};
201197

202-
const applyClerkJsScriptAttributes = (options: LoadClerkJsScriptOptions) => (script: HTMLScriptElement) => {
203-
const attributes = buildClerkJsScriptAttributes(options);
198+
export const buildClerkUiScriptAttributes = (options: LoadClerkUiScriptOptions) => {
199+
// TODO @nikos do we need this?
200+
return buildClerkJsScriptAttributes(options);
201+
};
202+
203+
const applyAttributesToScript = (attributes: Record<string, string>) => (script: HTMLScriptElement) => {
204204
for (const attribute in attributes) {
205205
script.setAttribute(attribute, attributes[attribute]);
206206
}

0 commit comments

Comments
 (0)