Skip to content

feat(shared,ui): Add phoneNumberCountryCode and preferredIdentifier to SignIn/SignUp#8199

Open
nikosdouvlis wants to merge 4 commits intomainfrom
nk/phone-country-preferred-identifier
Open

feat(shared,ui): Add phoneNumberCountryCode and preferredIdentifier to SignIn/SignUp#8199
nikosdouvlis wants to merge 4 commits intomainfrom
nk/phone-country-preferred-identifier

Conversation

@nikosdouvlis
Copy link
Copy Markdown
Member

@nikosdouvlis nikosdouvlis commented Mar 29, 2026

Why

There's currently no way for developers to preselect the country in the SignIn/SignUp phone dropdown, or to control which identifier type shows first without pre-filling a value. The only workaround is passing a full phone number in initialValues, which has the unwanted side effect of filling the input.

What changed

Two new developer-facing options:

  • initialValues.phoneNumberCountryCode (ISO 3166 alpha-2, e.g. 'GR'): Preselects the country in the phone dropdown without pre-filling the number. Threads through PhoneInput -> useFormattedPhoneNumber with precedence: parsed phone number > phoneNumberCountryCode > geo-IP (x-country header) > 'us'
  • appearance.options.preferredIdentifier ('emailAddress' | 'phoneNumber' | 'username'): Selects which identifier shows first on both SignIn and SignUp. Cascades from ClerkProvider. On SignIn, it controls which tab is selected. On SignUp, it breaks the tie when both email and phone are in the same tier (both required or both optional). Silently ignored if the identifier isn't enabled.

Target API:

<ClerkProvider appearance={{ options: { preferredIdentifier: 'phoneNumber' } }}>
  <SignIn initialValues={{ phoneNumberCountryCode: 'GR' }} />
  <SignUp />
</ClerkProvider>

Or per-component:

<SignIn
  initialValues={{ phoneNumberCountryCode: 'GR' }}
  appearance={{ options: { preferredIdentifier: 'phoneNumber' } }}
/>

Packages affected

  • @clerk/shared: New phoneNumberCountryCode field on SignInInitialValues, new key in SIGN_IN_INITIAL_VALUE_KEYS
  • @clerk/ui: New preferredIdentifier in appearance Options, prop threading through PhoneInput chain, identifier resolution in SignInStart and getInitialActiveIdentifier for SignUp

Summary by CodeRabbit

  • New Features

    • Preselect phone input country in SignIn/SignUp initial values using an ISO 3166-1 alpha-2 code.
    • Add an appearance option to prefer which sign-in/sign-up identifier (email, phone, or username) is shown first.
    • Phone input accepts a default country ISO to influence initial country selection.
  • Tests

    • Added tests for preferred-identifier tie-breaking and phone-country initialization/precedence.
  • Documentation

    • Added a changeset documenting the updates.

…nInIdentifier

Adds two new features to the SignIn component:

- `phoneNumberCountryCode` (in `initialValues`): Preselects the country
  in the phone dropdown using an ISO 3166 alpha-2 code, without pre-filling
  the phone number. Precedence: parsed phone > defaultCountryIso > geo-IP > 'us'.

- `preferredSignInIdentifier` (in `appearance.options`): Selects which
  identifier type (email, phone, username) shows first without pre-filling
  a value. Falls back to default ordering when the identifier is not enabled.
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 29, 2026

🦋 Changeset detected

Latest commit: d52bc82

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 21 packages
Name Type
@clerk/shared Minor
@clerk/ui Minor
@clerk/agent-toolkit Patch
@clerk/astro Patch
@clerk/backend Patch
@clerk/chrome-extension Patch
@clerk/clerk-js Patch
@clerk/expo-passkeys Patch
@clerk/expo Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/hono Patch
@clerk/localizations Patch
@clerk/msw Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/react Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/vue Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Mar 29, 2026 0:31am

Request Review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 29, 2026

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@8199

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8199

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8199

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8199

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8199

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8199

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8199

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8199

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8199

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8199

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8199

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8199

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8199

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8199

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8199

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8199

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8199

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8199

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8199

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8199

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8199

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8199

commit: d52bc82

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

Adds support for preselecting a phone country and preferring an identifier in SignIn/SignUp flows. Introduces optional phoneNumberCountryCode in SignInInitialValues and extends sign-in initial keys. Adds an optional preferredIdentifier to appearance Options and includes it in parsed options. Phone input components accept a defaultCountryIso prop and phone-formatting logic considers it. SignIn/SignUp initial identifier selection logic now respects preferredIdentifier and initial values. Tests were added/updated to validate these behaviors.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding phoneNumberCountryCode and preferredIdentifier to SignIn/SignUp components across shared and ui packages.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

…tifier

When both email and phone are in the same tier (both enabled and both
required, or both enabled and both optional), preferredIdentifier from
appearance.options now breaks the tie instead of hardcoding email-first.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/SignIn/SignInStart.tsx`:
- Around line 144-145: The function resolveInitialIdentifier currently may
return an empty string which violates the declared return type
SignInStartIdentifier and can create invalid state when used with
useState<SignInStartIdentifier>; update resolveInitialIdentifier to return
'email_address' as the fallback instead of '' so it always returns a valid
SignInStartIdentifier (this will fix the initial state passed to
useState<SignInStartIdentifier> in the SignInStart component).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 84915083-c921-4405-896b-1f96fc62dabc

📥 Commits

Reviewing files that changed from the base of the PR and between 495ce18 and 38dc379.

📒 Files selected for processing (5)
  • .changeset/phone-country-preferred-identifier.md
  • packages/ui/src/components/SignIn/SignInStart.tsx
  • packages/ui/src/components/SignIn/__tests__/SignInStart.test.tsx
  • packages/ui/src/customizables/parseAppearance.ts
  • packages/ui/src/internal/appearance.ts

Comment on lines +144 to +145
return identifierAttributes[0] || '';
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

SIGN_IN_FILE="$(fd -i 'SignInStart.tsx' packages/ui/src/components/SignIn | head -n1)"
COMMON_FILE="$(fd -i 'constants.ts' packages/ui/src/common | head -n1)"

echo "== SignInStartIdentifier definition =="
rg -n -C2 "export type SignInStartIdentifier" "$COMMON_FILE"

echo
echo "== Fallback returning empty string =="
rg -n -C2 "return identifierAttributes\\[0\\] \\|\\| ''" "$SIGN_IN_FILE"

Repository: clerk/javascript

Length of output: 616


🏁 Script executed:

#!/bin/bash
set -euo pipefail

SIGN_IN_FILE="$(fd -i 'SignInStart.tsx' packages/ui/src/components/SignIn | head -n1)"

echo "== Full function context around line 144 =="
sed -n '120,160p' "$SIGN_IN_FILE" | cat -n

echo
echo "== Search for function containing line 144 with return type =="
sed -n '100,150p' "$SIGN_IN_FILE" | cat -n

Repository: clerk/javascript

Length of output: 4320


🏁 Script executed:

#!/bin/bash
set -euo pipefail

SIGN_IN_FILE="$(fd -i 'SignInStart.tsx' packages/ui/src/components/SignIn | head -n1)"

echo "== identifierAttributes initialization and usage =="
rg -n "identifierAttributes" "$SIGN_IN_FILE" | head -20

Repository: clerk/javascript

Length of output: 950


🏁 Script executed:

#!/bin/bash
set -euo pipefail

SIGN_IN_FILE="$(fd -i 'SignInStart.tsx' packages/ui/src/components/SignIn | head -n1)"

echo "== identifierAttributes initialization (lines 94-110) =="
sed -n '94,110p' "$SIGN_IN_FILE" | cat -n

Repository: clerk/javascript

Length of output: 889


🏁 Script executed:

#!/bin/bash
set -euo pipefail

COMMON_FILE="$(fd -i 'constants.ts' packages/ui/src/common | head -n1)"

echo "== groupIdentifiers function definition =="
rg -n -A15 "export const groupIdentifiers" "$COMMON_FILE"

Repository: clerk/javascript

Length of output: 896


🏁 Script executed:

#!/bin/bash
set -euo pipefail

COMMON_FILE="$(fd -i 'constants.ts' packages/ui/src/common | head -n1)"

echo "== Attribute type definition =="
rg -n "export.*Attribute" "$COMMON_FILE" | head -20

echo
echo "== enabledFirstFactorIdentifiers related =="
rg -n "enabledFirstFactorIdentifiers" "$COMMON_FILE" | head -10

Repository: clerk/javascript

Length of output: 227


🏁 Script executed:

#!/bin/bash
set -euo pipefail

SIGN_IN_FILE="$(fd -i 'SignInStart.tsx' packages/ui/src/components/SignIn | head -n1)"

echo "== userSettings type and initialization =="
rg -n "userSettings" "$SIGN_IN_FILE" | head -15

echo
echo "== Search for Attribute type definition =="
rg -n "type Attribute\|interface Attribute" packages/ui/src

Repository: clerk/javascript

Length of output: 1235


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Search for Attribute type in packages/ui =="
rg -n "type Attribute|interface Attribute" packages/ui/src --type ts --type tsx

echo
echo "== Search broader for Attribute definition =="
rg -n "Attribute" packages/ui/src/common/constants.ts | head -5

Repository: clerk/javascript

Length of output: 151


🏁 Script executed:

#!/bin/bash
set -euo pipefail

COMMON_FILE="$(fd -i 'constants.ts' packages/ui/src/common | head -n1)"

echo "== Full constants.ts file (first 100 lines) =="
head -100 "$COMMON_FILE" | cat -n

echo
echo "== Search for Attribute import/type =="
rg "Attribute" "$COMMON_FILE" | head -10

Repository: clerk/javascript

Length of output: 5092


Fix contract violation: resolveInitialIdentifier returns invalid empty string

Line 144-145 returns '', but the function's declared return type SignInStartIdentifier does not include empty string. This violates the type contract and creates invalid state when passed to useState<SignInStartIdentifier> at line 28.

Use 'email_address' as the fallback since it's always a valid identifier per the type definition:

Proposed fix
-    return identifierAttributes[0] || '';
+    const fallback = identifierAttributes[0];
+    if (fallback) {
+      return fallback;
+    }
+    return 'email_address';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return identifierAttributes[0] || '';
};
const fallback = identifierAttributes[0];
if (fallback) {
return fallback;
}
return 'email_address';
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/SignIn/SignInStart.tsx` around lines 144 - 145,
The function resolveInitialIdentifier currently may return an empty string which
violates the declared return type SignInStartIdentifier and can create invalid
state when used with useState<SignInStartIdentifier>; update
resolveInitialIdentifier to return 'email_address' as the fallback instead of ''
so it always returns a valid SignInStartIdentifier (this will fix the initial
state passed to useState<SignInStartIdentifier> in the SignInStart component).

@nikosdouvlis nikosdouvlis changed the title feat(shared,ui): Add phoneNumberCountryCode and preferredSignInIdentifier to SignIn feat(shared,ui): Add phoneNumberCountryCode and preferredIdentifier to SignIn/SignUp Mar 29, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/internal/appearance.ts`:
- Around line 967-973: The public option preferredIdentifier currently lists
'username' but SignUp doesn't support it; either implement username support or
remove it from the public API. To implement: update the SignUp selection logic
in signUpFormHelpers (the filter that limits identifiers) to include 'username',
extend the ActiveIdentifier type to allow 'username', and ensure the SignUp
component/form renders and validates a username input and that selection logic
(e.g., default ordering) respects preferredIdentifier; to remove: delete
'username' from the preferredIdentifier union and update any docs/types that
advertise it so the API and implementation stay consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: e1089f86-c5e8-4cf9-b606-292a0bcc64ba

📥 Commits

Reviewing files that changed from the base of the PR and between 3ecf92d and d52bc82.

📒 Files selected for processing (1)
  • packages/ui/src/internal/appearance.ts

Comment on lines +967 to +973
/**
* Controls which identifier type is shown first on the SignIn and SignUp components
* when multiple identifiers are enabled. Does not prefill any value.
*
* @default undefined
*/
preferredIdentifier?: 'emailAddress' | 'phoneNumber' | 'username';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

preferredIdentifier: 'username' is exposed here but not actually supported by SignUp.

Options now advertises 'username' for SignUp/SignIn, but SignUp currently drops it (packages/ui/src/components/SignUp/signUpFormHelpers.ts, Line 120 filters to email/phone only, and ActiveIdentifier cannot be 'username'). This creates a silent contract break for consumers using the new API.

Please either implement 'username' handling in SignUp selection logic/types or remove 'username' from this public option/docs until supported.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/internal/appearance.ts` around lines 967 - 973, The public
option preferredIdentifier currently lists 'username' but SignUp doesn't support
it; either implement username support or remove it from the public API. To
implement: update the SignUp selection logic in signUpFormHelpers (the filter
that limits identifiers) to include 'username', extend the ActiveIdentifier type
to allow 'username', and ensure the SignUp component/form renders and validates
a username input and that selection logic (e.g., default ordering) respects
preferredIdentifier; to remove: delete 'username' from the preferredIdentifier
union and update any docs/types that advertise it so the API and implementation
stay consistent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant