diff --git a/.changeset/gold-lamps-appear.md b/.changeset/gold-lamps-appear.md
new file mode 100644
index 00000000000..bdacd44a2da
--- /dev/null
+++ b/.changeset/gold-lamps-appear.md
@@ -0,0 +1,6 @@
+---
+"@clerk/clerk-js": minor
+"@clerk/types": minor
+---
+
+Hide sign up url from `` component when mode is `restricted`
\ No newline at end of file
diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json
index bbb0e027c5c..f0f2208f716 100644
--- a/packages/clerk-js/bundlewatch.config.json
+++ b/packages/clerk-js/bundlewatch.config.json
@@ -1,6 +1,6 @@
{
"files": [
- { "path": "./dist/clerk.browser.js", "maxSize": "64kB" },
+ { "path": "./dist/clerk.browser.js", "maxSize": "64.1kB" },
{ "path": "./dist/clerk.headless.js", "maxSize": "43kB" },
{ "path": "./dist/ui-common*.js", "maxSize": "86KB" },
{ "path": "./dist/vendors*.js", "maxSize": "70KB" },
diff --git a/packages/clerk-js/src/core/constants.ts b/packages/clerk-js/src/core/constants.ts
index 82a8c3f3207..a025cfc8ce9 100644
--- a/packages/clerk-js/src/core/constants.ts
+++ b/packages/clerk-js/src/core/constants.ts
@@ -1,3 +1,5 @@
+import type { SignUpModes } from '@clerk/types';
+
// TODO: Do we still have a use for this or can we simply preserve all params?
export const PRESERVED_QUERYSTRING_PARAMS = [
'redirect_url',
@@ -29,3 +31,8 @@ export const SIGN_IN_INITIAL_VALUE_KEYS = ['email_address', 'phone_number', 'use
export const SIGN_UP_INITIAL_VALUE_KEYS = ['email_address', 'phone_number', 'username', 'first_name', 'last_name'];
export const DEBOUNCE_MS = 350;
+
+export const SIGN_UP_MODES: Record = {
+ PUBLIC: 'public',
+ RESTRICTED: 'restricted',
+};
diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
index d2b6f838356..4bfb6c6dd57 100644
--- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
+++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
@@ -3,7 +3,7 @@ import { isWebAuthnAutofillSupported, isWebAuthnSupported } from '@clerk/shared/
import type { ClerkAPIError, SignInCreateParams, SignInResource } from '@clerk/types';
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
-import { ERROR_CODES } from '../../../core/constants';
+import { ERROR_CODES, SIGN_UP_MODES } from '../../../core/constants';
import { clerkInvalidFAPIResponse } from '../../../core/errors';
import { getClerkQueryParam, removeClerkQueryParam } from '../../../utils';
import type { SignInStartIdentifier } from '../../common';
@@ -410,13 +410,15 @@ export function _SignInStart(): JSX.Element {
-
-
-
-
+ {userSettings.signUp.mode === SIGN_UP_MODES.PUBLIC && (
+
+
+
+
+ )}
diff --git a/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx b/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
index 8ee9c1e1cea..b88ad62ae38 100644
--- a/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
+++ b/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx
@@ -111,6 +111,29 @@ describe('SignInStart', () => {
});
});
+ describe('Restricted mode', () => {
+ it('"Don\'t have an account?" text should not be presented', async () => {
+ const { wrapper } = await createFixtures(f => {
+ f.withEmailAddress();
+ f.withRestrictedMode();
+ });
+ render(, { wrapper });
+ expect(screen.queryByText(/Don’t have an account/i)).not.toBeInTheDocument();
+ });
+
+ it('"Don\'t have an account?" text should be visible', async () => {
+ const { wrapper, fixtures } = await createFixtures(f => {
+ f.withEmailAddress();
+ });
+ render(, { wrapper });
+
+ const signUpLink = screen.getByText(/Don’t have an account/i).nextElementSibling;
+ expect(signUpLink?.textContent).toBe('Sign up');
+ expect(signUpLink?.tagName.toUpperCase()).toBe('A');
+ expect(signUpLink?.getAttribute('href')).toMatch(fixtures.environment.displayConfig.signUpUrl);
+ });
+ });
+
describe('Social OAuth', () => {
it.each(OAUTH_PROVIDERS)('shows the "Continue with $name" social OAuth button', async ({ provider, name }) => {
const { wrapper } = await createFixtures(f => {
diff --git a/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts b/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts
index 54bf863a191..f429976a28c 100644
--- a/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts
+++ b/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts
@@ -17,6 +17,7 @@ import type {
VerificationJSON,
} from '@clerk/types';
+import { SIGN_UP_MODES } from '../../../core/constants';
import type { OrgParams } from '../../../core/test/fixtures';
import { createUser, getOrganizationId } from '../../../core/test/fixtures';
import { createUserFixture } from './fixtures';
@@ -317,6 +318,8 @@ const createUserSettingsFixtureHelpers = (environment: EnvironmentJSON) => {
show_zxcvbn: false,
min_zxcvbn_strength: 0,
};
+ us.sign_up.mode = SIGN_UP_MODES.PUBLIC;
+
const emptyAttribute = {
first_factors: [],
second_factors: [],
@@ -476,6 +479,10 @@ const createUserSettingsFixtureHelpers = (environment: EnvironmentJSON) => {
};
};
+ const withRestrictedMode = () => {
+ us.sign_up.mode = SIGN_UP_MODES.RESTRICTED;
+ };
+
// TODO: Add the rest, consult pkg/generate/auth_config.go
return {
@@ -493,5 +500,6 @@ const createUserSettingsFixtureHelpers = (environment: EnvironmentJSON) => {
withAuthenticatorApp,
withPasskey,
withPasskeySettings,
+ withRestrictedMode,
};
};
diff --git a/packages/types/src/userSettings.ts b/packages/types/src/userSettings.ts
index ca61c419537..39770917446 100644
--- a/packages/types/src/userSettings.ts
+++ b/packages/types/src/userSettings.ts
@@ -47,10 +47,14 @@ export type SignInData = {
};
};
+export type SignUpModes = 'public' | 'restricted';
+
export type SignUpData = {
allowlist_only: boolean;
progressive: boolean;
captcha_enabled: boolean;
+ invite_only_enabled: boolean;
+ mode: SignUpModes;
};
export type PasswordSettingsData = {
diff --git a/packages/ui/src/components/sign-in/steps/start.tsx b/packages/ui/src/components/sign-in/steps/start.tsx
index 6c0d18ebd5d..9c162242db1 100644
--- a/packages/ui/src/components/sign-in/steps/start.tsx
+++ b/packages/ui/src/components/sign-in/steps/start.tsx
@@ -11,12 +11,14 @@ import { PhoneNumberField } from '~/common/phone-number-field';
import { PhoneNumberOrUsernameField } from '~/common/phone-number-or-username-field';
import { UsernameField } from '~/common/username-field';
import { LOCALIZATION_NEEDED } from '~/constants/localizations';
+import { SIGN_UP_MODES } from '~/constants/user-settings';
import { useAppearance } from '~/contexts';
import { useAttributes } from '~/hooks/use-attributes';
import { useCard } from '~/hooks/use-card';
import { useDevModeWarning } from '~/hooks/use-dev-mode-warning';
import { useDisplayConfig } from '~/hooks/use-display-config';
import { useEnabledConnections } from '~/hooks/use-enabled-connections';
+import { useEnvironment } from '~/hooks/use-environment';
import { useLocalizations } from '~/hooks/use-localizations';
import { Button } from '~/primitives/button';
import * as Card from '~/primitives/card';
@@ -27,6 +29,7 @@ import { Separator } from '~/primitives/separator';
export function SignInStart() {
const enabledConnections = useEnabledConnections();
const { t } = useLocalizations();
+ const { userSettings } = useEnvironment();
const { enabled: usernameEnabled } = useAttributes('username');
const { enabled: phoneNumberEnabled } = useAttributes('phone_number');
const { enabled: emailAddressEnabled } = useAttributes('email_address');
@@ -184,12 +187,14 @@ export function SignInStart() {
-
-
- {t('signIn.start.actionText')}{' '}
- {t('signIn.start.actionLink')}
-
-
+ {userSettings.signUp.mode === SIGN_UP_MODES.PUBLIC ? (
+
+
+ {t('signIn.start.actionText')}{' '}
+ {t('signIn.start.actionLink')}
+
+
+ ) : null}
diff --git a/packages/ui/src/constants/user-settings.ts b/packages/ui/src/constants/user-settings.ts
new file mode 100644
index 00000000000..54db661f8b1
--- /dev/null
+++ b/packages/ui/src/constants/user-settings.ts
@@ -0,0 +1,6 @@
+import type { SignUpModes } from '@clerk/types';
+
+export const SIGN_UP_MODES: Record = {
+ PUBLIC: 'public',
+ RESTRICTED: 'restricted',
+};