From b933a2ba8112aefbabd7fe3313b89e083452d2dd Mon Sep 17 00:00:00 2001 From: Bryce Kalow Date: Fri, 20 Dec 2024 14:32:41 -0600 Subject: [PATCH] feat(clerk-react): Render button components before clerk-js load (#4810) Co-authored-by: Alex Carpenter --- .changeset/sixty-moose-jog.md | 5 ++ .../react/src/components/SignInButton.tsx | 73 +++++++++-------- .../components/SignInWithMetamaskButton.tsx | 2 +- .../react/src/components/SignOutButton.tsx | 2 +- .../react/src/components/SignUpButton.tsx | 79 ++++++++++--------- 5 files changed, 86 insertions(+), 75 deletions(-) create mode 100644 .changeset/sixty-moose-jog.md diff --git a/.changeset/sixty-moose-jog.md b/.changeset/sixty-moose-jog.md new file mode 100644 index 0000000000..d653d62f77 --- /dev/null +++ b/.changeset/sixty-moose-jog.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-react': minor +--- + +Allow ``, `, ``, and `` to render while clerk-js is still loading. This reduces any layout shift that might be caused by these components not rendering immediately. diff --git a/packages/react/src/components/SignInButton.tsx b/packages/react/src/components/SignInButton.tsx index 6e33f8ba28..1036cbef43 100644 --- a/packages/react/src/components/SignInButton.tsx +++ b/packages/react/src/components/SignInButton.tsx @@ -5,45 +5,48 @@ import type { SignInButtonProps, WithClerkProp } from '../types'; import { assertSingleChild, normalizeWithDefaultValue, safeExecute } from '../utils'; import { withClerk } from './withClerk'; -export const SignInButton = withClerk(({ clerk, children, ...props }: WithClerkProp) => { - const { - signUpFallbackRedirectUrl, - forceRedirectUrl, - fallbackRedirectUrl, - signUpForceRedirectUrl, - mode, - initialValues, - ...rest - } = props; - children = normalizeWithDefaultValue(children, 'Sign in'); - const child = assertSingleChild(children)('SignInButton'); - - const clickHandler = () => { - const opts: SignInProps = { +export const SignInButton = withClerk( + ({ clerk, children, ...props }: WithClerkProp) => { + const { + signUpFallbackRedirectUrl, forceRedirectUrl, fallbackRedirectUrl, - signUpFallbackRedirectUrl, signUpForceRedirectUrl, + mode, initialValues, - }; + ...rest + } = props; + children = normalizeWithDefaultValue(children, 'Sign in'); + const child = assertSingleChild(children)('SignInButton'); - if (mode === 'modal') { - return clerk.openSignIn(opts); - } - return clerk.redirectToSignIn({ - ...opts, - signInFallbackRedirectUrl: fallbackRedirectUrl, - signInForceRedirectUrl: forceRedirectUrl, - }); - }; + const clickHandler = () => { + const opts: SignInProps = { + forceRedirectUrl, + fallbackRedirectUrl, + signUpFallbackRedirectUrl, + signUpForceRedirectUrl, + initialValues, + }; - const wrappedChildClickHandler: React.MouseEventHandler = async e => { - if (child && typeof child === 'object' && 'props' in child) { - await safeExecute(child.props.onClick)(e); - } - return clickHandler(); - }; + if (mode === 'modal') { + return clerk.openSignIn(opts); + } + return clerk.redirectToSignIn({ + ...opts, + signInFallbackRedirectUrl: fallbackRedirectUrl, + signInForceRedirectUrl: forceRedirectUrl, + }); + }; + + const wrappedChildClickHandler: React.MouseEventHandler = async e => { + if (child && typeof child === 'object' && 'props' in child) { + await safeExecute(child.props.onClick)(e); + } + return clickHandler(); + }; - const childProps = { ...rest, onClick: wrappedChildClickHandler }; - return React.cloneElement(child as React.ReactElement, childProps); -}, 'SignInButton'); + const childProps = { ...rest, onClick: wrappedChildClickHandler }; + return React.cloneElement(child as React.ReactElement, childProps); + }, + { component: 'SignInButton', renderWhileLoading: true }, +); diff --git a/packages/react/src/components/SignInWithMetamaskButton.tsx b/packages/react/src/components/SignInWithMetamaskButton.tsx index 80148d0635..9776e8d55e 100644 --- a/packages/react/src/components/SignInWithMetamaskButton.tsx +++ b/packages/react/src/components/SignInWithMetamaskButton.tsx @@ -28,5 +28,5 @@ export const SignInWithMetamaskButton = withClerk( const childProps = { ...rest, onClick: wrappedChildClickHandler }; return React.cloneElement(child as React.ReactElement, childProps); }, - 'SignInWithMetamask', + { component: 'SignInWithMetamask', renderWhileLoading: true }, ); diff --git a/packages/react/src/components/SignOutButton.tsx b/packages/react/src/components/SignOutButton.tsx index 6676684eba..8ff8378906 100644 --- a/packages/react/src/components/SignOutButton.tsx +++ b/packages/react/src/components/SignOutButton.tsx @@ -27,5 +27,5 @@ export const SignOutButton = withClerk( const childProps = { ...rest, onClick: wrappedChildClickHandler }; return React.cloneElement(child as React.ReactElement, childProps); }, - 'SignOutButton', + { component: 'SignOutButton', renderWhileLoading: true }, ); diff --git a/packages/react/src/components/SignUpButton.tsx b/packages/react/src/components/SignUpButton.tsx index bb8540add1..757439c24d 100644 --- a/packages/react/src/components/SignUpButton.tsx +++ b/packages/react/src/components/SignUpButton.tsx @@ -5,49 +5,52 @@ import type { SignUpButtonProps, WithClerkProp } from '../types'; import { assertSingleChild, normalizeWithDefaultValue, safeExecute } from '../utils'; import { withClerk } from './withClerk'; -export const SignUpButton = withClerk(({ clerk, children, ...props }: WithClerkProp) => { - const { - fallbackRedirectUrl, - forceRedirectUrl, - signInFallbackRedirectUrl, - signInForceRedirectUrl, - mode, - unsafeMetadata, - initialValues, - ...rest - } = props; - - children = normalizeWithDefaultValue(children, 'Sign up'); - const child = assertSingleChild(children)('SignUpButton'); - - const clickHandler = () => { - const opts: SignUpProps = { +export const SignUpButton = withClerk( + ({ clerk, children, ...props }: WithClerkProp) => { + const { fallbackRedirectUrl, forceRedirectUrl, signInFallbackRedirectUrl, signInForceRedirectUrl, + mode, unsafeMetadata, initialValues, + ...rest + } = props; + + children = normalizeWithDefaultValue(children, 'Sign up'); + const child = assertSingleChild(children)('SignUpButton'); + + const clickHandler = () => { + const opts: SignUpProps = { + fallbackRedirectUrl, + forceRedirectUrl, + signInFallbackRedirectUrl, + signInForceRedirectUrl, + unsafeMetadata, + initialValues, + }; + + if (mode === 'modal') { + return clerk.openSignUp(opts); + } + + return clerk.redirectToSignUp({ + ...opts, + signUpFallbackRedirectUrl: fallbackRedirectUrl, + signUpForceRedirectUrl: forceRedirectUrl, + }); }; - if (mode === 'modal') { - return clerk.openSignUp(opts); - } - - return clerk.redirectToSignUp({ - ...opts, - signUpFallbackRedirectUrl: fallbackRedirectUrl, - signUpForceRedirectUrl: forceRedirectUrl, - }); - }; - - const wrappedChildClickHandler: React.MouseEventHandler = async e => { - if (child && typeof child === 'object' && 'props' in child) { - await safeExecute(child.props.onClick)(e); - } - return clickHandler(); - }; - - const childProps = { ...rest, onClick: wrappedChildClickHandler }; - return React.cloneElement(child as React.ReactElement, childProps); -}, 'SignUpButton'); + const wrappedChildClickHandler: React.MouseEventHandler = async e => { + if (child && typeof child === 'object' && 'props' in child) { + await safeExecute(child.props.onClick)(e); + } + return clickHandler(); + }; + + const childProps = { ...rest, onClick: wrappedChildClickHandler }; + return React.cloneElement(child as React.ReactElement, childProps); + }, + { component: 'SignUpButton', renderWhileLoading: true }, +);