diff --git a/.changeset/itchy-mirrors-decide.md b/.changeset/itchy-mirrors-decide.md
new file mode 100644
index 0000000000..dfd472eed6
--- /dev/null
+++ b/.changeset/itchy-mirrors-decide.md
@@ -0,0 +1,5 @@
+---
+'@clerk/elements': patch
+---
+
+Fix forms unable to submit upon re-mounting
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 1b9197f9c0..e9c4538028 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
@@ -2,6 +2,7 @@
import * as Clerk from '@clerk/elements/common';
import * as SignUp from '@clerk/elements/sign-up';
+import Link from 'next/link';
import type { ComponentProps } from 'react';
import { H1, HR as Hr, P } from '@/components/design';
@@ -42,6 +43,16 @@ export default function SignUpPage() {
Sign Up
+
+ Have an account?{' '}
+
+ Sign In
+
+
+
Sign Up
+
+ Have an account?{' '}
+
+ Sign In
+
+
+
{
+ enqueue.assign({
+ formRef: event.formRef,
+ });
+
+ // Reset the current step, to reset the form reference.
+ enqueue.raise({ type: 'RESET.STEP' });
+ }),
+ },
'NAVIGATE.PREVIOUS': '.Hist',
'NAVIGATE.START': '.Start',
LOADING: {
@@ -292,6 +303,10 @@ export const SignInRouterMachine = setup({
},
},
on: {
+ 'RESET.STEP': {
+ target: 'Start',
+ reenter: true,
+ },
NEXT: [
{
guard: 'isComplete',
@@ -329,6 +344,10 @@ export const SignInRouterMachine = setup({
},
},
on: {
+ 'RESET.STEP': {
+ target: 'FirstFactor',
+ reenter: true,
+ },
NEXT: [
{
guard: 'isComplete',
@@ -399,6 +418,10 @@ export const SignInRouterMachine = setup({
},
},
on: {
+ 'RESET.STEP': {
+ target: 'SecondFactor',
+ reenter: true,
+ },
NEXT: [
{
guard: 'isComplete',
@@ -426,6 +449,10 @@ export const SignInRouterMachine = setup({
},
},
on: {
+ 'RESET.STEP': {
+ target: 'ResetPassword',
+ reenter: true,
+ },
NEXT: [
{
guard: 'isComplete',
diff --git a/packages/elements/src/internals/machines/sign-in/router.types.ts b/packages/elements/src/internals/machines/sign-in/router.types.ts
index ada057c95c..d1ea1209ea 100644
--- a/packages/elements/src/internals/machines/sign-in/router.types.ts
+++ b/packages/elements/src/internals/machines/sign-in/router.types.ts
@@ -5,12 +5,14 @@ import type { TFormMachine } from '~/internals/machines/form';
import type {
BaseRouterContext,
BaseRouterErrorEvent,
+ BaseRouterFormAttachEvent,
BaseRouterInput,
BaseRouterLoadingEvent,
BaseRouterNextEvent,
BaseRouterPrevEvent,
BaseRouterRedirectEvent,
BaseRouterResetEvent,
+ BaseRouterResetStepEvent,
BaseRouterSetClerkEvent,
BaseRouterStartEvent,
BaseRouterTransferEvent,
@@ -47,6 +49,7 @@ export type SignInRouterSystemId = keyof typeof SignInRouterSystemId;
// ---------------------------------- Events ---------------------------------- //
+export type SignInRouterFormAttachEvent = BaseRouterFormAttachEvent;
export type SignInRouterNextEvent = BaseRouterNextEvent;
export type SignInRouterStartEvent = BaseRouterStartEvent;
export type SignInRouterPrevEvent = BaseRouterPrevEvent;
@@ -56,6 +59,7 @@ export type SignInRouterErrorEvent = BaseRouterErrorEvent;
export type SignInRouterTransferEvent = BaseRouterTransferEvent;
export type SignInRouterRedirectEvent = BaseRouterRedirectEvent;
export type SignInRouterResetEvent = BaseRouterResetEvent;
+export type SignInRouterResetStepEvent = BaseRouterResetStepEvent;
export type SignInRouterLoadingEvent = BaseRouterLoadingEvent<'start' | 'verifications' | 'reset-password'>;
export type SignInRouterSetClerkEvent = BaseRouterSetClerkEvent;
export type SignInRouterSubmitEvent = { type: 'SUBMIT' };
@@ -73,6 +77,7 @@ export type SignInRouterNavigationEvents =
| SignInRouterPrevEvent;
export type SignInRouterEvents =
+ | SignInRouterFormAttachEvent
| SignInRouterInitEvent
| SignInRouterNextEvent
| SignInRouterNavigationEvents
@@ -80,6 +85,7 @@ export type SignInRouterEvents =
| SignInRouterTransferEvent
| SignInRouterRedirectEvent
| SignInRouterResetEvent
+ | SignInRouterResetStepEvent
| SignInVerificationFactorUpdateEvent
| SignInRouterLoadingEvent
| SignInRouterSetClerkEvent
diff --git a/packages/elements/src/internals/machines/sign-up/router.machine.ts b/packages/elements/src/internals/machines/sign-up/router.machine.ts
index 79503b4539..8f67f4953c 100644
--- a/packages/elements/src/internals/machines/sign-up/router.machine.ts
+++ b/packages/elements/src/internals/machines/sign-up/router.machine.ts
@@ -181,6 +181,17 @@ export const SignUpRouterMachine = setup({
params: { strategy: 'saml' },
}),
},
+ 'FORM.ATTACH': {
+ description: 'Attach/re-attach the form to the router.',
+ actions: enqueueActions(({ enqueue, event }) => {
+ enqueue.assign({
+ formRef: event.formRef,
+ });
+
+ // Reset the current step, to reset the form reference.
+ enqueue.raise({ type: 'RESET.STEP' });
+ }),
+ },
'NAVIGATE.PREVIOUS': '.Hist',
'NAVIGATE.START': '.Start',
LOADING: {
@@ -274,6 +285,10 @@ export const SignUpRouterMachine = setup({
},
},
on: {
+ 'RESET.STEP': {
+ target: 'Start',
+ reenter: true,
+ },
NEXT: [
{
guard: 'isStatusComplete',
@@ -307,6 +322,10 @@ export const SignUpRouterMachine = setup({
},
},
on: {
+ 'RESET.STEP': {
+ target: 'Continue',
+ reenter: true,
+ },
NEXT: [
{
guard: 'isStatusComplete',
@@ -355,6 +374,10 @@ export const SignUpRouterMachine = setup({
},
],
on: {
+ 'RESET.STEP': {
+ target: 'Verification',
+ reenter: true,
+ },
NEXT: [
{
guard: 'isStatusComplete',
diff --git a/packages/elements/src/internals/machines/sign-up/router.types.ts b/packages/elements/src/internals/machines/sign-up/router.types.ts
index 2ea4fd4b0b..cd0b69a8bd 100644
--- a/packages/elements/src/internals/machines/sign-up/router.types.ts
+++ b/packages/elements/src/internals/machines/sign-up/router.types.ts
@@ -5,12 +5,14 @@ import type { TFormMachine } from '~/internals/machines/form';
import type {
BaseRouterContext,
BaseRouterErrorEvent,
+ BaseRouterFormAttachEvent,
BaseRouterInput,
BaseRouterLoadingEvent,
BaseRouterNextEvent,
BaseRouterPrevEvent,
BaseRouterRedirectEvent,
BaseRouterResetEvent,
+ BaseRouterResetStepEvent,
BaseRouterSetClerkEvent,
BaseRouterStartEvent,
BaseRouterTransferEvent,
@@ -41,6 +43,7 @@ export type SignUpRouterSystemId = keyof typeof SignUpRouterSystemId;
// ---------------------------------- Events ---------------------------------- //
+export type SignUpRouterFormAttachEvent = BaseRouterFormAttachEvent;
export type SignUpRouterNextEvent = BaseRouterNextEvent;
export type SignUpRouterStartEvent = BaseRouterStartEvent;
export type SignUpRouterPrevEvent = BaseRouterPrevEvent;
@@ -48,6 +51,7 @@ export type SignUpRouterErrorEvent = BaseRouterErrorEvent;
export type SignUpRouterTransferEvent = BaseRouterTransferEvent;
export type SignUpRouterRedirectEvent = BaseRouterRedirectEvent;
export type SignUpRouterResetEvent = BaseRouterResetEvent;
+export type SignUpRouterResetStepEvent = BaseRouterResetStepEvent;
export type SignUpRouterLoadingEvent = BaseRouterLoadingEvent<'start' | 'verifications' | 'continue'>;
export type SignUpRouterSetClerkEvent = BaseRouterSetClerkEvent;
@@ -60,6 +64,7 @@ export interface SignUpRouterInitEvent extends BaseRouterInput {
export type SignUpRouterNavigationEvents = SignUpRouterStartEvent | SignUpRouterPrevEvent;
export type SignUpRouterEvents =
+ | SignUpRouterFormAttachEvent
| SignUpRouterInitEvent
| SignUpRouterNextEvent
| SignUpRouterNavigationEvents
@@ -67,6 +72,7 @@ export type SignUpRouterEvents =
| SignUpRouterTransferEvent
| SignUpRouterRedirectEvent
| SignUpRouterResetEvent
+ | SignUpRouterResetStepEvent
| SignUpRouterLoadingEvent
| SignUpRouterSetClerkEvent;
diff --git a/packages/elements/src/internals/machines/types/router.types.ts b/packages/elements/src/internals/machines/types/router.types.ts
index 904fb56dd4..f28ad58477 100644
--- a/packages/elements/src/internals/machines/types/router.types.ts
+++ b/packages/elements/src/internals/machines/types/router.types.ts
@@ -6,8 +6,10 @@ import type {
SignInStrategy,
Web3Strategy,
} from '@clerk/types';
+import type { ActorRefFrom } from 'xstate';
import type { ClerkElementsError } from '~/internals/errors';
+import type { TFormMachine } from '~/internals/machines/form';
import type { ClerkRouter } from '~/react/router';
// ---------------------------------- Events ---------------------------------- //
@@ -15,9 +17,11 @@ import type { ClerkRouter } from '~/react/router';
export type BaseRouterLoadingStep = 'start' | 'verifications' | 'continue' | 'reset-password';
export type BaseRouterNextEvent = { type: 'NEXT'; resource?: T };
+export type BaseRouterFormAttachEvent = { type: 'FORM.ATTACH'; formRef: ActorRefFrom };
export type BaseRouterPrevEvent = { type: 'NAVIGATE.PREVIOUS' };
export type BaseRouterStartEvent = { type: 'NAVIGATE.START' };
export type BaseRouterResetEvent = { type: 'RESET' };
+export type BaseRouterResetStepEvent = { type: 'RESET.STEP' };
export type BaseRouterErrorEvent = { type: 'ERROR'; error: Error };
export type BaseRouterTransferEvent = { type: 'TRANSFER' };
export type BaseRouterLoadingEvent = (
diff --git a/packages/elements/src/react/sign-in/root.tsx b/packages/elements/src/react/sign-in/root.tsx
index b33fa50a69..09cce2c872 100644
--- a/packages/elements/src/react/sign-in/root.tsx
+++ b/packages/elements/src/react/sign-in/root.tsx
@@ -45,6 +45,14 @@ function SignInFlowProvider({ children, exampleMode }: SignInFlowProviderProps)
}
});
+ // Ensure that the latest instantiated formRef is attached to the router
+ if (formRef && actor.getSnapshot().can({ type: 'RESET.STEP' })) {
+ actor.send({
+ type: 'FORM.ATTACH',
+ formRef,
+ });
+ }
+
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [clerk, exampleMode, formRef?.id, !!router]);
diff --git a/packages/elements/src/react/sign-up/root.tsx b/packages/elements/src/react/sign-up/root.tsx
index a3c25da212..4a0cc57ad7 100644
--- a/packages/elements/src/react/sign-up/root.tsx
+++ b/packages/elements/src/react/sign-up/root.tsx
@@ -20,13 +20,13 @@ type SignUpFlowProviderProps = {
};
const actor = createActor(SignUpRouterMachine, { inspect });
-const ref = actor.start();
+actor.start();
function SignUpFlowProvider({ children, exampleMode }: SignUpFlowProviderProps) {
const clerk = useClerk();
const router = useClerkRouter();
const formRef = useFormStore();
- const isReady = useSelector(ref, state => state.value !== 'Idle');
+ const isReady = useSelector(actor, state => state.value !== 'Idle');
useEffect(() => {
if (!clerk || !router) return;
@@ -42,14 +42,22 @@ function SignUpFlowProvider({ children, exampleMode }: SignUpFlowProviderProps)
signInPath: SIGN_IN_DEFAULT_BASE_PATH,
};
- if (ref.getSnapshot().can(evt)) {
- ref.send(evt);
+ if (actor.getSnapshot().can(evt)) {
+ actor.send(evt);
+ }
+
+ // Ensure that the latest instantiated formRef is attached to the router
+ if (formRef && actor.getSnapshot().can({ type: 'RESET.STEP' })) {
+ actor.send({
+ type: 'FORM.ATTACH',
+ formRef,
+ });
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [clerk, exampleMode, formRef?.id, !!router]);
- return isReady ? {children} : null;
+ return isReady ? {children} : null;
}
export type SignUpRootProps = SignUpFlowProviderProps & {