diff --git a/.changeset/friendly-dots-join.md b/.changeset/friendly-dots-join.md new file mode 100644 index 00000000000..5937307a445 --- /dev/null +++ b/.changeset/friendly-dots-join.md @@ -0,0 +1,5 @@ +--- +"@clerk/elements": patch +--- + +Fix Sign In & Sign Up root fallbacks not rendering as expected diff --git a/packages/elements/examples/nextjs/app/sign-up/[[...sign-up]]/page.tsx b/packages/elements/examples/nextjs/app/sign-up/[[...sign-up]]/page.tsx index 950cc211042..aeaf6ca5734 100644 --- a/packages/elements/examples/nextjs/app/sign-up/[[...sign-up]]/page.tsx +++ b/packages/elements/examples/nextjs/app/sign-up/[[...sign-up]]/page.tsx @@ -69,14 +69,6 @@ export default function SignUpPage() { Sign In with Google - - - - Sign In with Metamask -
@@ -142,14 +134,6 @@ export default function SignUpPage() { Sign In with Google - - - - Sign In with Metamask -
diff --git a/packages/elements/src/react/common/form/index.tsx b/packages/elements/src/react/common/form/index.tsx index 9267b11a4b4..796d2bd5db0 100644 --- a/packages/elements/src/react/common/form/index.tsx +++ b/packages/elements/src/react/common/form/index.tsx @@ -356,7 +356,8 @@ const useInput = ({ const FORM_NAME = 'ClerkElementsForm'; type FormElement = React.ElementRef; -type FormProps = SetRequired & { +type FormProps = Omit & { + children: React.ReactNode; flowActor?: BaseActorRef<{ type: 'SUBMIT' }>; }; diff --git a/packages/elements/src/react/sign-in/root.tsx b/packages/elements/src/react/sign-in/root.tsx index 82d860ab2e7..94292b8c54b 100644 --- a/packages/elements/src/react/sign-in/root.tsx +++ b/packages/elements/src/react/sign-in/root.tsx @@ -1,5 +1,6 @@ -import { ClerkLoaded, ClerkLoading, useClerk } from '@clerk/clerk-react'; +import { useClerk } from '@clerk/clerk-react'; import { eventComponentMounted } from '@clerk/shared/telemetry'; +import { useSelector } from '@xstate/react'; import React, { useEffect } from 'react'; import { createActor } from 'xstate'; @@ -17,15 +18,21 @@ import { usePathnameWithoutCatchAll } from '../utils/path-inference/next'; type SignInFlowProviderProps = { children: React.ReactNode; exampleMode?: boolean; + /** + * Fallback markup to render while Clerk is loading + */ + fallback?: React.ReactNode; + isRootPath: boolean; }; const actor = createActor(SignInRouterMachine, { inspect }); actor.start(); -function SignInFlowProvider({ children, exampleMode }: SignInFlowProviderProps) { +function SignInFlowProvider({ children, exampleMode, fallback, isRootPath }: SignInFlowProviderProps) { const clerk = useClerk(); const router = useClerkRouter(); const formRef = useFormStore(); + const isReady = useSelector(actor, state => state.value !== 'Idle'); useEffect(() => { if (!clerk || !router) { @@ -57,16 +64,17 @@ function SignInFlowProvider({ children, exampleMode }: SignInFlowProviderProps) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [clerk, exampleMode, formRef?.id, !!router]); + }, [clerk, exampleMode, formRef?.id, !!router, clerk.loaded]); - return {children}; + return ( + + {isRootPath && !isReady && fallback ?
{fallback}
: null} + {clerk.loaded && isReady ? children : null} +
+ ); } -export type SignInRootProps = SignInFlowProviderProps & { - /** - * Fallback markup to render while Clerk is loading - */ - fallback?: React.ReactNode; +export type SignInRootProps = Omit & { /** * The base path for your sign-in route. * Will be automatically inferred in Next.js. @@ -125,13 +133,12 @@ export function SignInRoot({ router={router} > - - {isRootPath ? ( - -
{fallback}
-
- ) : null} - {children} + + {children}
diff --git a/packages/elements/src/react/sign-up/root.tsx b/packages/elements/src/react/sign-up/root.tsx index 4ce7f86526a..9cea15248d4 100644 --- a/packages/elements/src/react/sign-up/root.tsx +++ b/packages/elements/src/react/sign-up/root.tsx @@ -1,4 +1,4 @@ -import { ClerkLoaded, ClerkLoading, useClerk } from '@clerk/clerk-react'; +import { useClerk } from '@clerk/clerk-react'; import { eventComponentMounted } from '@clerk/shared/telemetry'; import { useSelector } from '@xstate/react'; import { useEffect } from 'react'; @@ -18,12 +18,17 @@ import { usePathnameWithoutCatchAll } from '../utils/path-inference/next'; type SignUpFlowProviderProps = { children: React.ReactNode; exampleMode?: boolean; + /** + * Fallback markup to render while Clerk is loading + */ + fallback?: React.ReactNode; + isRootPath: boolean; }; const actor = createActor(SignUpRouterMachine, { inspect }); actor.start(); -function SignUpFlowProvider({ children, exampleMode }: SignUpFlowProviderProps) { +function SignUpFlowProvider({ children, exampleMode, fallback, isRootPath }: SignUpFlowProviderProps) { const clerk = useClerk(); const router = useClerkRouter(); const formRef = useFormStore(); @@ -58,16 +63,17 @@ function SignUpFlowProvider({ children, exampleMode }: SignUpFlowProviderProps) } }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [clerk, exampleMode, formRef?.id, !!router]); + }, [clerk, exampleMode, formRef?.id, !!router, clerk.loaded]); - return isReady ? {children} : null; + return ( + + {isRootPath && !isReady && fallback ?
{fallback}
: null} + {clerk.loaded && isReady ? children : null} +
+ ); } -export type SignUpRootProps = SignUpFlowProviderProps & { - /** - * Fallback markup to render while Clerk is loading - */ - fallback?: React.ReactNode; +export type SignUpRootProps = Omit & { /** * The base path for your sign-up route. * Will be automatically inferred in Next.js. @@ -126,13 +132,12 @@ export function SignUpRoot({ router={router} > - - {isRootPath ? ( - -
{fallback}
-
- ) : null} - {children} + + {children}