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.