Skip to content

Commit b933a2b

Browse files
feat(clerk-react): Render button components before clerk-js load (#4810)
Co-authored-by: Alex Carpenter <[email protected]>
1 parent b5eb15b commit b933a2b

File tree

5 files changed

+86
-75
lines changed

5 files changed

+86
-75
lines changed

.changeset/sixty-moose-jog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-react': minor
3+
---
4+
5+
Allow `<SignInButton />`, <SignUpButton />`, `<SignOutButton />`, and `<SignInWithMetamaskButton />` to render while clerk-js is still loading. This reduces any layout shift that might be caused by these components not rendering immediately.

packages/react/src/components/SignInButton.tsx

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,48 @@ import type { SignInButtonProps, WithClerkProp } from '../types';
55
import { assertSingleChild, normalizeWithDefaultValue, safeExecute } from '../utils';
66
import { withClerk } from './withClerk';
77

8-
export const SignInButton = withClerk(({ clerk, children, ...props }: WithClerkProp<SignInButtonProps>) => {
9-
const {
10-
signUpFallbackRedirectUrl,
11-
forceRedirectUrl,
12-
fallbackRedirectUrl,
13-
signUpForceRedirectUrl,
14-
mode,
15-
initialValues,
16-
...rest
17-
} = props;
18-
children = normalizeWithDefaultValue(children, 'Sign in');
19-
const child = assertSingleChild(children)('SignInButton');
20-
21-
const clickHandler = () => {
22-
const opts: SignInProps = {
8+
export const SignInButton = withClerk(
9+
({ clerk, children, ...props }: WithClerkProp<SignInButtonProps>) => {
10+
const {
11+
signUpFallbackRedirectUrl,
2312
forceRedirectUrl,
2413
fallbackRedirectUrl,
25-
signUpFallbackRedirectUrl,
2614
signUpForceRedirectUrl,
15+
mode,
2716
initialValues,
28-
};
17+
...rest
18+
} = props;
19+
children = normalizeWithDefaultValue(children, 'Sign in');
20+
const child = assertSingleChild(children)('SignInButton');
2921

30-
if (mode === 'modal') {
31-
return clerk.openSignIn(opts);
32-
}
33-
return clerk.redirectToSignIn({
34-
...opts,
35-
signInFallbackRedirectUrl: fallbackRedirectUrl,
36-
signInForceRedirectUrl: forceRedirectUrl,
37-
});
38-
};
22+
const clickHandler = () => {
23+
const opts: SignInProps = {
24+
forceRedirectUrl,
25+
fallbackRedirectUrl,
26+
signUpFallbackRedirectUrl,
27+
signUpForceRedirectUrl,
28+
initialValues,
29+
};
3930

40-
const wrappedChildClickHandler: React.MouseEventHandler = async e => {
41-
if (child && typeof child === 'object' && 'props' in child) {
42-
await safeExecute(child.props.onClick)(e);
43-
}
44-
return clickHandler();
45-
};
31+
if (mode === 'modal') {
32+
return clerk.openSignIn(opts);
33+
}
34+
return clerk.redirectToSignIn({
35+
...opts,
36+
signInFallbackRedirectUrl: fallbackRedirectUrl,
37+
signInForceRedirectUrl: forceRedirectUrl,
38+
});
39+
};
40+
41+
const wrappedChildClickHandler: React.MouseEventHandler = async e => {
42+
if (child && typeof child === 'object' && 'props' in child) {
43+
await safeExecute(child.props.onClick)(e);
44+
}
45+
return clickHandler();
46+
};
4647

47-
const childProps = { ...rest, onClick: wrappedChildClickHandler };
48-
return React.cloneElement(child as React.ReactElement<unknown>, childProps);
49-
}, 'SignInButton');
48+
const childProps = { ...rest, onClick: wrappedChildClickHandler };
49+
return React.cloneElement(child as React.ReactElement<unknown>, childProps);
50+
},
51+
{ component: 'SignInButton', renderWhileLoading: true },
52+
);

packages/react/src/components/SignInWithMetamaskButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ export const SignInWithMetamaskButton = withClerk(
2828
const childProps = { ...rest, onClick: wrappedChildClickHandler };
2929
return React.cloneElement(child as React.ReactElement<unknown>, childProps);
3030
},
31-
'SignInWithMetamask',
31+
{ component: 'SignInWithMetamask', renderWhileLoading: true },
3232
);

packages/react/src/components/SignOutButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@ export const SignOutButton = withClerk(
2727
const childProps = { ...rest, onClick: wrappedChildClickHandler };
2828
return React.cloneElement(child as React.ReactElement<unknown>, childProps);
2929
},
30-
'SignOutButton',
30+
{ component: 'SignOutButton', renderWhileLoading: true },
3131
);

packages/react/src/components/SignUpButton.tsx

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,52 @@ import type { SignUpButtonProps, WithClerkProp } from '../types';
55
import { assertSingleChild, normalizeWithDefaultValue, safeExecute } from '../utils';
66
import { withClerk } from './withClerk';
77

8-
export const SignUpButton = withClerk(({ clerk, children, ...props }: WithClerkProp<SignUpButtonProps>) => {
9-
const {
10-
fallbackRedirectUrl,
11-
forceRedirectUrl,
12-
signInFallbackRedirectUrl,
13-
signInForceRedirectUrl,
14-
mode,
15-
unsafeMetadata,
16-
initialValues,
17-
...rest
18-
} = props;
19-
20-
children = normalizeWithDefaultValue(children, 'Sign up');
21-
const child = assertSingleChild(children)('SignUpButton');
22-
23-
const clickHandler = () => {
24-
const opts: SignUpProps = {
8+
export const SignUpButton = withClerk(
9+
({ clerk, children, ...props }: WithClerkProp<SignUpButtonProps>) => {
10+
const {
2511
fallbackRedirectUrl,
2612
forceRedirectUrl,
2713
signInFallbackRedirectUrl,
2814
signInForceRedirectUrl,
15+
mode,
2916
unsafeMetadata,
3017
initialValues,
18+
...rest
19+
} = props;
20+
21+
children = normalizeWithDefaultValue(children, 'Sign up');
22+
const child = assertSingleChild(children)('SignUpButton');
23+
24+
const clickHandler = () => {
25+
const opts: SignUpProps = {
26+
fallbackRedirectUrl,
27+
forceRedirectUrl,
28+
signInFallbackRedirectUrl,
29+
signInForceRedirectUrl,
30+
unsafeMetadata,
31+
initialValues,
32+
};
33+
34+
if (mode === 'modal') {
35+
return clerk.openSignUp(opts);
36+
}
37+
38+
return clerk.redirectToSignUp({
39+
...opts,
40+
signUpFallbackRedirectUrl: fallbackRedirectUrl,
41+
signUpForceRedirectUrl: forceRedirectUrl,
42+
});
3143
};
3244

33-
if (mode === 'modal') {
34-
return clerk.openSignUp(opts);
35-
}
36-
37-
return clerk.redirectToSignUp({
38-
...opts,
39-
signUpFallbackRedirectUrl: fallbackRedirectUrl,
40-
signUpForceRedirectUrl: forceRedirectUrl,
41-
});
42-
};
43-
44-
const wrappedChildClickHandler: React.MouseEventHandler = async e => {
45-
if (child && typeof child === 'object' && 'props' in child) {
46-
await safeExecute(child.props.onClick)(e);
47-
}
48-
return clickHandler();
49-
};
50-
51-
const childProps = { ...rest, onClick: wrappedChildClickHandler };
52-
return React.cloneElement(child as React.ReactElement<unknown>, childProps);
53-
}, 'SignUpButton');
45+
const wrappedChildClickHandler: React.MouseEventHandler = async e => {
46+
if (child && typeof child === 'object' && 'props' in child) {
47+
await safeExecute(child.props.onClick)(e);
48+
}
49+
return clickHandler();
50+
};
51+
52+
const childProps = { ...rest, onClick: wrappedChildClickHandler };
53+
return React.cloneElement(child as React.ReactElement<unknown>, childProps);
54+
},
55+
{ component: 'SignUpButton', renderWhileLoading: true },
56+
);

0 commit comments

Comments
 (0)