From 5c01f8a1dfc4a840c46094ae84f2a8e76e7484a4 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:23:36 -0500 Subject: [PATCH 1/8] chore(ui): Add type-check to build step --- packages/ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/package.json b/packages/ui/package.json index af98e76b752..d24567d5094 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -69,7 +69,7 @@ "register" ], "scripts": { - "build": "pnpm build:umd && pnpm build:esm && pnpm check:no-rhc", + "build": "pnpm build:umd && pnpm build:esm && pnpm check:no-rhc && pnpm type-check", "build:analyze": "rspack build --config rspack.config.js --env production --env analyze --analyze", "build:esm": "tsdown", "build:rsdoctor": "RSDOCTOR=true rspack build --config rspack.config.js --env production", From fa518b4a98cec9768e191d7fa38080423c3b0f42 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:39:09 -0500 Subject: [PATCH 2/8] fix(ui): Type getContiner correctly --- packages/ui/src/Components.tsx | 35 +++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/packages/ui/src/Components.tsx b/packages/ui/src/Components.tsx index 858f707cd38..a73d85c5dba 100644 --- a/packages/ui/src/Components.tsx +++ b/packages/ui/src/Components.tsx @@ -8,14 +8,18 @@ import type { __internal_UserVerificationProps, Clerk, ClerkOptions, - CreateOrganizationProps, + CreateOrganizationModalProps, EnvironmentResource, GoogleOneTapProps, - OrganizationProfileProps, + OrganizationProfileModalProps, SignInProps, + SignInModalProps, SignUpProps, + SignUpModalProps, + UserProfileModalProps, UserProfileProps, WaitlistProps, + WaitlistModalProps, } from '@clerk/shared/types'; import { createDeferredPromise } from '@clerk/shared/utils'; import React, { Suspense, useCallback, useRef, useSyncExternalStore } from 'react'; @@ -161,16 +165,16 @@ interface ComponentsState { appearance: Appearance | undefined; options: ClerkOptions | undefined; googleOneTapModal: null | GoogleOneTapProps; - signInModal: null | SignInProps; - signUpModal: null | SignUpProps; - userProfileModal: null | UserProfileProps; + signInModal: null | SignInModalProps; + signUpModal: null | SignUpModalProps; + userProfileModal: null | UserProfileModalProps; userVerificationModal: null | __internal_UserVerificationProps; - organizationProfileModal: null | OrganizationProfileProps; - createOrganizationModal: null | CreateOrganizationProps; + organizationProfileModal: null | OrganizationProfileModalProps; + createOrganizationModal: null | CreateOrganizationModalProps; enableOrganizationsPromptModal: null | __internal_EnableOrganizationsPromptProps; blankCaptchaModal: null; organizationSwitcherPrefetch: boolean; - waitlistModal: null | WaitlistProps; + waitlistModal: null | WaitlistModalProps; checkoutDrawer: { open: false; props: null | __internal_CheckoutProps; @@ -502,7 +506,7 @@ const Components = (props: ComponentsProps) => { onClose={() => componentsControls.closeModal('signIn')} onExternalNavigate={() => componentsControls.closeModal('signIn')} startPath={buildVirtualRouterUrl({ base: '/sign-in', path: urlStateParam?.path })} - getContainer={signInModal?.getContainer} + getContainer={signInModal?.getContainer ?? (() => null)} componentName={'SignInModal'} > @@ -520,7 +524,7 @@ const Components = (props: ComponentsProps) => { onClose={() => componentsControls.closeModal('signUp')} onExternalNavigate={() => componentsControls.closeModal('signUp')} startPath={buildVirtualRouterUrl({ base: '/sign-up', path: urlStateParam?.path })} - getContainer={signUpModal?.getContainer} + getContainer={signUpModal?.getContainer ?? (() => null)} componentName={'SignUpModal'} > @@ -541,7 +545,7 @@ const Components = (props: ComponentsProps) => { base: '/user', path: userProfileModal?.__experimental_startPath || urlStateParam?.path, })} - getContainer={userProfileModal?.getContainer} + getContainer={userProfileModal?.getContainer ?? (() => null)} componentName={'UserProfileModal'} modalContainerSx={{ alignItems: 'center' }} modalContentSx={t => ({ height: `min(${t.sizes.$176}, calc(100% - ${t.sizes.$12}))`, margin: 0 })} @@ -559,7 +563,7 @@ const Components = (props: ComponentsProps) => { onClose={() => componentsControls.closeModal('userVerification')} onExternalNavigate={() => componentsControls.closeModal('userVerification')} startPath={buildVirtualRouterUrl({ base: '/user-verification', path: urlStateParam?.path })} - getContainer={userVerificationModal?.getContainer} + getContainer={userVerificationModal?.getContainer ?? (() => null)} componentName={'UserVerificationModal'} modalContainerSx={{ alignItems: 'center' }} > @@ -579,7 +583,7 @@ const Components = (props: ComponentsProps) => { base: '/organizationProfile', path: organizationProfileModal?.__experimental_startPath || urlStateParam?.path, })} - getContainer={organizationProfileModal?.getContainer} + getContainer={organizationProfileModal?.getContainer ?? (() => null)} componentName={'OrganizationProfileModal'} modalContainerSx={{ alignItems: 'center' }} modalContentSx={t => ({ height: `min(${t.sizes.$176}, calc(100% - ${t.sizes.$12}))`, margin: 0 })} @@ -597,7 +601,7 @@ const Components = (props: ComponentsProps) => { onClose={() => componentsControls.closeModal('createOrganization')} onExternalNavigate={() => componentsControls.closeModal('createOrganization')} startPath={buildVirtualRouterUrl({ base: '/createOrganization', path: urlStateParam?.path })} - getContainer={createOrganizationModal?.getContainer} + getContainer={createOrganizationModal?.getContainer ?? (() => null)} componentName={'CreateOrganizationModal'} modalContainerSx={{ alignItems: 'center' }} modalContentSx={t => ({ height: `min(${t.sizes.$120}, calc(100% - ${t.sizes.$12}))`, margin: 0 })} @@ -615,7 +619,7 @@ const Components = (props: ComponentsProps) => { onClose={() => componentsControls.closeModal('waitlist')} onExternalNavigate={() => componentsControls.closeModal('waitlist')} startPath={buildVirtualRouterUrl({ base: '/waitlist', path: urlStateParam?.path })} - getContainer={waitlistModal?.getContainer} + getContainer={waitlistModal?.getContainer ?? (() => null)} componentName={'WaitlistModal'} > @@ -638,6 +642,7 @@ const Components = (props: ComponentsProps) => { canCloseModal={false} modalId={'cl-modal-captcha-wrapper'} modalStyle={{ visibility: 'hidden', pointerEvents: 'none' }} + getContainer={() => null} > From 42302db76549bae8a706a19ab6f5a31912cb0a2a Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:46:40 -0500 Subject: [PATCH 3/8] fix(ui): assert non-null fee when planPeriod is monthly --- packages/ui/src/components/Checkout/CheckoutForm.tsx | 3 ++- .../components/PaymentAttempts/PaymentAttemptPage.tsx | 3 ++- .../src/components/PricingTable/PricingTableMatrix.tsx | 6 ++++-- packages/ui/src/components/SubscriptionDetails/index.tsx | 9 ++++++--- .../src/components/Subscriptions/SubscriptionsList.tsx | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/ui/src/components/Checkout/CheckoutForm.tsx b/packages/ui/src/components/Checkout/CheckoutForm.tsx index 7fc80d0979f..ce57964e9e4 100644 --- a/packages/ui/src/components/Checkout/CheckoutForm.tsx +++ b/packages/ui/src/components/Checkout/CheckoutForm.tsx @@ -42,7 +42,8 @@ export const CheckoutForm = withCardStateProvider(() => { const fee = planPeriod === 'month' - ? plan.fee + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + plan.fee! : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion plan.annualMonthlyFee!; diff --git a/packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx b/packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx index 3cee693d74b..2ebd2973aa2 100644 --- a/packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx +++ b/packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx @@ -205,7 +205,8 @@ function PaymentAttemptBody({ subscriptionItem }: { subscriptionItem: BillingSub const fee = subscriptionItem.planPeriod === 'month' - ? subscriptionItem.plan.fee + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + subscriptionItem.plan.fee! : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion subscriptionItem.plan.annualMonthlyFee!; diff --git a/packages/ui/src/components/PricingTable/PricingTableMatrix.tsx b/packages/ui/src/components/PricingTable/PricingTableMatrix.tsx index 4666b96d894..6227f385b6c 100644 --- a/packages/ui/src/components/PricingTable/PricingTableMatrix.tsx +++ b/packages/ui/src/components/PricingTable/PricingTableMatrix.tsx @@ -157,10 +157,12 @@ export function PricingTableMatrix({ {plans.map(plan => { const highlight = plan.slug === highlightedPlan; const planFee = !plan.annualMonthlyFee - ? plan.fee + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + plan.fee! : planPeriod === 'annual' ? plan.annualMonthlyFee - : plan.fee; + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + plan.fee!; return ( { openCheckout({ @@ -508,7 +510,8 @@ const SubscriptionCard = ({ subscription }: { subscription: BillingSubscriptionI const fee = subscription.planPeriod === 'month' - ? subscription.plan.fee + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + subscription.plan.fee! : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion subscription.plan.annualFee!; diff --git a/packages/ui/src/components/Subscriptions/SubscriptionsList.tsx b/packages/ui/src/components/Subscriptions/SubscriptionsList.tsx index 43dc1b4a052..4f3831f896a 100644 --- a/packages/ui/src/components/Subscriptions/SubscriptionsList.tsx +++ b/packages/ui/src/components/Subscriptions/SubscriptionsList.tsx @@ -154,7 +154,7 @@ export function SubscriptionsList({ function SubscriptionRow({ subscription, length }: { subscription: BillingSubscriptionItemResource; length: number }) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const fee = subscription.planPeriod === 'annual' ? subscription.plan.annualFee! : subscription.plan.fee; + const fee = subscription.planPeriod === 'annual' ? subscription.plan.annualFee! : subscription.plan.fee!; const { captionForSubscription } = usePlansContext(); const feeFormatted = useMemo(() => { From 56f854d895834109781d99cb729a6475b584d98f Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:50:17 -0500 Subject: [PATCH 4/8] fix(ui): Allow null organizationCreationDefaults --- .../tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx | 2 +- .../OrganizationCreationDefaultsAlert.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx index df4dc2f4592..256825efcad 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/CreateOrganizationScreen.tsx @@ -26,7 +26,7 @@ import { OrganizationCreationDefaultsAlert } from './OrganizationCreationDefault type CreateOrganizationScreenProps = { onCancel?: () => void; - organizationCreationDefaults?: OrganizationCreationDefaultsResource; + organizationCreationDefaults?: OrganizationCreationDefaultsResource | null; }; export const CreateOrganizationScreen = (props: CreateOrganizationScreenProps) => { diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx index efef8ee9bb1..4bc120a9191 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskChooseOrganization/OrganizationCreationDefaultsAlert.tsx @@ -6,7 +6,7 @@ import { localizationKeys } from '@/localization'; export function OrganizationCreationDefaultsAlert({ organizationCreationDefaults, }: { - organizationCreationDefaults?: OrganizationCreationDefaultsResource; + organizationCreationDefaults?: OrganizationCreationDefaultsResource | null; }) { const localizationKey = advisoryToLocalizationKey(organizationCreationDefaults?.advisory); if (!localizationKey) { From 4e202209d19ee563991ac41a6f93c644861bcabd Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:50:51 -0500 Subject: [PATCH 5/8] fix(ui): cast as type --- .../SessionTasks/tasks/TaskSetupMfa/SetupMfaStartScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/SetupMfaStartScreen.tsx b/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/SetupMfaStartScreen.tsx index 57f099e76a0..6d735baffdc 100644 --- a/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/SetupMfaStartScreen.tsx +++ b/packages/ui/src/components/SessionTasks/tasks/TaskSetupMfa/SetupMfaStartScreen.tsx @@ -60,7 +60,7 @@ export const SetupMfaStartScreen = withCardStateProvider((props: SetupMfaStartSc })} > {availableMethods.map(method => { - const methodConfig = METHOD_CONFIG[method] ?? null; + const methodConfig = METHOD_CONFIG[method as keyof typeof METHOD_CONFIG] ?? null; if (!methodConfig) { return null; From 699d2d220691c2757f8b198d6990ab67a4740f30 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:51:52 -0500 Subject: [PATCH 6/8] fix(ui): remove unused import --- packages/ui/src/internal/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/ui/src/internal/index.ts b/packages/ui/src/internal/index.ts index 064695f56c8..d86140b52a0 100644 --- a/packages/ui/src/internal/index.ts +++ b/packages/ui/src/internal/index.ts @@ -1,7 +1,5 @@ import type { ClerkUIConstructor } from '@clerk/shared/ui'; -import type { Appearance } from './appearance'; - export type { ComponentControls, MountComponentRenderer } from '../Components'; export type { WithInternalRouting } from './routing'; From 3b620a10675113cdbd7f1d334a2ad9c0d631302a Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:56:56 -0500 Subject: [PATCH 7/8] chore(ui): Ignore typechecking in test folder --- packages/ui/tsconfig.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index b1cdc8add12..c97f2d88d06 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -29,6 +29,14 @@ "@/ui*": ["./src/*"] } }, - "exclude": ["node_modules", "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx", "**/__tests__/**"], + "exclude": [ + "node_modules", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.spec.ts", + "**/*.spec.tsx", + "**/__tests__/**", + "./src/test/**" + ], "include": ["src", "src/global.d.ts"] } From e99e1eff31e38dc962b85dc1f2581f07af20663f Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:58:10 -0500 Subject: [PATCH 8/8] chore(repo): Add changeset --- .changeset/clean-views-admire.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/clean-views-admire.md diff --git a/.changeset/clean-views-admire.md b/.changeset/clean-views-admire.md new file mode 100644 index 00000000000..92a49ba4a25 --- /dev/null +++ b/.changeset/clean-views-admire.md @@ -0,0 +1,5 @@ +--- +'@clerk/ui': patch +--- + +Fix minor internal issues with TypeScript types.