feat(shared,ui): Add phoneNumberCountryCode and preferredIdentifier to SignIn/SignUp#8199
feat(shared,ui): Add phoneNumberCountryCode and preferredIdentifier to SignIn/SignUp#8199nikosdouvlis wants to merge 4 commits intomainfrom
Conversation
…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 detectedLatest commit: d52bc82 The changes in this PR will be included in the next version bump. This PR includes changesets to release 21 packages
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
📝 WalkthroughWalkthroughAdds support for preselecting a phone country and preferring an identifier in SignIn/SignUp flows. Introduces optional 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
…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.
There was a problem hiding this comment.
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
📒 Files selected for processing (5)
.changeset/phone-country-preferred-identifier.mdpackages/ui/src/components/SignIn/SignInStart.tsxpackages/ui/src/components/SignIn/__tests__/SignInStart.test.tsxpackages/ui/src/customizables/parseAppearance.tspackages/ui/src/internal/appearance.ts
| return identifierAttributes[0] || ''; | ||
| }; |
There was a problem hiding this comment.
🧩 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 -nRepository: 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 -20Repository: 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 -nRepository: 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 -10Repository: 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/srcRepository: 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 -5Repository: 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 -10Repository: 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.
| 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).
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
packages/ui/src/internal/appearance.ts
| /** | ||
| * 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'; |
There was a problem hiding this comment.
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.
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 throughPhoneInput->useFormattedPhoneNumberwith precedence: parsed phone number >phoneNumberCountryCode> geo-IP (x-countryheader) >'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:
Or per-component:
Packages affected
@clerk/shared: NewphoneNumberCountryCodefield onSignInInitialValues, new key inSIGN_IN_INITIAL_VALUE_KEYS@clerk/ui: NewpreferredIdentifierin appearanceOptions, prop threading throughPhoneInputchain, identifier resolution inSignInStartandgetInitialActiveIdentifierfor SignUpSummary by CodeRabbit
New Features
Tests
Documentation