diff --git a/.changeset/ripe-months-stay.md b/.changeset/ripe-months-stay.md new file mode 100644 index 00000000000..8628a563620 --- /dev/null +++ b/.changeset/ripe-months-stay.md @@ -0,0 +1,5 @@ +--- +'@clerk/clerk-js': patch +--- + +Fix layout shift when navigating after task resolution diff --git a/packages/clerk-js/src/ui/components/SessionTasks/index.tsx b/packages/clerk-js/src/ui/components/SessionTasks/index.tsx index ab7f9191a06..2ab37fa68b3 100644 --- a/packages/clerk-js/src/ui/components/SessionTasks/index.tsx +++ b/packages/clerk-js/src/ui/components/SessionTasks/index.tsx @@ -1,6 +1,6 @@ import { useClerk } from '@clerk/shared/react'; import { eventComponentMounted } from '@clerk/shared/telemetry'; -import { useCallback, useContext, useEffect, useState } from 'react'; +import { useCallback, useContext, useEffect, useRef, useState } from 'react'; import { Card } from '@/ui/elements/Card'; import { withCardStateProvider } from '@/ui/elements/contexts'; @@ -57,6 +57,7 @@ export const SessionTask = withCardStateProvider(() => { const signInContext = useContext(SignInContext); const signUpContext = useContext(SignUpContext); const [isNavigatingToTask, setIsNavigatingToTask] = useState(false); + const currentTaskContainer = useRef(null); const redirectUrlComplete = signInContext?.afterSignInUrl ?? signUpContext?.afterSignUpUrl ?? clerk?.buildAfterSignInUrl(); @@ -88,8 +89,12 @@ export const SessionTask = withCardStateProvider(() => { if (!clerk.session?.currentTask) { return ( - - + ({ + minHeight: currentTaskContainer ? currentTaskContainer.current?.offsetHeight : undefined, + })} + > + ({ flex: 1 })}> @@ -98,7 +103,7 @@ export const SessionTask = withCardStateProvider(() => { } return ( - + ); diff --git a/packages/clerk-js/src/ui/components/SessionTasks/tasks/ForceOrganizationSelection.tsx b/packages/clerk-js/src/ui/components/SessionTasks/tasks/ForceOrganizationSelection.tsx index 04b2f4a769e..5f98dba91eb 100644 --- a/packages/clerk-js/src/ui/components/SessionTasks/tasks/ForceOrganizationSelection.tsx +++ b/packages/clerk-js/src/ui/components/SessionTasks/tasks/ForceOrganizationSelection.tsx @@ -3,6 +3,7 @@ import type { PropsWithChildren } from 'react'; import { useEffect, useRef, useState } from 'react'; import { OrganizationListContext } from '@/ui/contexts'; +import { useSessionTasksContext } from '@/ui/contexts/components/SessionTasks'; import { Card } from '@/ui/elements/Card'; import { useCardState, withCardStateProvider } from '@/ui/elements/contexts'; @@ -103,9 +104,10 @@ const CreateOrganizationPage = ({ currentFlow }: CommonPageProps) => { const FlowCard = ({ children }: PropsWithChildren) => { const card = useCardState(); + const { currentTaskContainer } = useSessionTasksContext(); return ( - + ({ padding: `${t.space.$8} ${t.space.$none} ${t.space.$none}` })}> ({ margin: `${t.space.$none} ${t.space.$5}` })}>{card.error} {children} diff --git a/packages/clerk-js/src/ui/types.ts b/packages/clerk-js/src/ui/types.ts index 04cf9ee6366..5c13694a8b8 100644 --- a/packages/clerk-js/src/ui/types.ts +++ b/packages/clerk-js/src/ui/types.ts @@ -132,6 +132,7 @@ export type CheckoutCtx = __internal_CheckoutProps & { export type SessionTasksCtx = { nextTask: () => Promise; redirectUrlComplete?: string; + currentTaskContainer: React.RefObject | null; }; export type OAuthConsentCtx = __internal_OAuthConsentProps & {