- Advanced Toast
- ArrowButton
- Arrows
- Avatar
- Backdrop
- Basic Toast
- Breadcrumb
- Button
- CardBackground
- Card
- ChatButton
- Checkbox
- Checkboxes
- Chip
- ChoiceExpander
- Clickable
- Date Field
- Dropdown
- ElasticHeight
- ExpandableCard
- Filter
- FormCheckbox
- Form Control
- Form Label
- HamburgerIcon
- Heading
- Image
- Link
- Loader
- Number Field
- Pagination
- Password
- Phone Field
- Popin
- Portal
- Prose
- Radio Group
- Radio
- Range
- Select
- SidebarLayout
- Spinner
- Switch
- Tabs
- Tag
- TextField
- ValidationMessage
'use client';
import { PhoneField } from '@/ui/forms/PhoneField';
export function PhoneFieldDemo() {
return (
<div className="flex flex-col gap-16">
<PhoneField
label="Phone Number"
description="Enter your phone number"
placeholder="+33 1 23 45 67 89"
mode="full"
className="max-w-sm"
pattern="+33 # ## ## ## ##"
/>
<PhoneField
label="Phone Number"
description="Select your code and number"
placeholder="1 23 45 67 89"
mode="split"
className="max-w-sm"
pattern="# ## ## ## ##"
/>
</div>
);
}
Installation
npx shadcn@latest add https://develop.trident-ui.pro.clubmed/r/phone-field.json
Usage
Full Mode
Single input field with pattern-based formatting for consistent phone number display.
'use client';
import { useState } from 'react';
import { PhoneField } from '@/docs/lib/ui/forms/phone-field';
export function Component() {
const [phone, setPhone] = useState<PhoneValue | null>(null);
return (
<PhoneField
label="Phone Number"
description="We'll send a verification code to this number"
mode="full"
pattern="## ## ## ## ##"
placeholder="Enter phone number"
onChange={(name, value) => setPhone(value)}
required
/>
);
}Split Mode
Separate prefix selector and number input for international phone numbers.
'use client';
import { useState } from 'react';
import { PhoneField } from '@/docs/lib/ui/forms/phone-field';
export function Component() {
const [phone, setPhone] = useState<PhoneValue | null>(null);
return (
<PhoneField
label="International Phone"
mode="split"
defaultPrefix="+1"
pattern="## ## ## ## ##"
placeholder="Enter phone number"
onChange={(name, value) => {
// value.full: "+1 12 34 56 78 90"
// value.prefix: "+1"
// value.number: "12 34 56 78 90"
// value.raw: "11234567890" (for backend)
setPhone(value);
}}
/>
);
}API Reference
PhoneField
| Prop | Type | Default | Description |
|---|---|---|---|
mode | "full" | "split" | "full" | Display mode. "full" shows single input with pattern. "split" shows prefix dropdown + number input. |
pattern | string | "## ## ## ## ##" | Format pattern where # represents digit placeholder. Example: "+1 (###) ###-####" or "## ## ## ## ##". |
prefixes | PhonePrefix[] | DEFAULT_PREFIXES | Array of country code prefixes for split mode. Only used when mode="split". |
defaultPrefix | string | First prefix in array | Default selected country code in split mode. Must match one of the prefixes values. |
label | string | undefined | The label text displayed above the input field. |
description | string | undefined | Helper text displayed below the label to provide additional context. |
placeholder | string | "" | Placeholder text shown when the input is empty. |
id | string | Auto-generated | The unique identifier for the input field. Auto-generated if not provided. |
name | string | Same as id | The name attribute for the input field, used in form submissions. Defaults to the id value. |
value | PhoneValue | undefined | The current phone value. See PhoneValue type below. |
onChange | (name: string, value: PhoneValue) => void | undefined | Callback function called when the phone number changes. Receives the field name and a PhoneValue object. |
icon | IconicNames | undefined | Optional icon name from @clubmed/trident-icons to display on the left side of the input (full mode only). |
iconType | IconicTypes | undefined | Optional icon type variant for the icon. |
disabled | boolean | false | Whether the field is disabled. Disabled fields have reduced opacity and cannot be interacted with. |
required | boolean | false | Whether the field is required. Displays a red asterisk next to the label. |
hideRequiredStar | boolean | false | When true, hides the required asterisk even if the field is required. |
validationStatus | "default" | "error" | "success" | "default" | The validation state of the field. Changes border color and shows status icons. |
errorMessage | string | undefined | Error message text displayed when validationStatus is "error". |
dataTestId | string | "PhoneField" | Custom test ID for the component wrapper, useful for testing. |
className | string | undefined | Additional CSS classes for the component wrapper. |
The component also accepts all standard HTML input attributes via spreading (...rest).
PhoneValue Type
The value and onChange props work with a structured PhoneValue object:
interface PhoneValue {
full: string; // Complete formatted number: "+33 12 34 56 78"
prefix?: string; // Country code (split mode only): "+33"
number?: string; // Formatted number (split mode only): "12 34 56 78"
raw: string; // Digits only, for backend: "33123456789"
}PhonePrefix Type
When using split mode with custom prefixes:
interface PhonePrefix {
code: string; // e.g., "+33"
label?: string; // e.g., "France (+33)"
country?: string; // e.g., "FR" (ISO country code)
}Notes
Pattern Formatting
The component formats phone numbers in real-time as users type. The pattern prop uses # as a placeholder for digits, with other characters treated as literals:
"## ## ## ## ##"→12 34 56 78 90"+1 (###) ###-####"→+1 (555) 123-4567"(###) ###-####"→(555) 123-4567
The component automatically:
- Extracts only digits from user input
- Applies the pattern format
- Maintains cursor position while typing
- Prevents input exceeding pattern length
Mode Comparison
Full Mode (mode="full"):
- Single input field with pattern-based formatting
- Best for national phone numbers with consistent format
- Simpler UI for users in single country
- Example:
12 34 56 78 90
Split Mode (mode="split"):
- Separate prefix dropdown and number input
- Best for international phone numbers
- Users can select country code from dropdown
- Example:
+33(dropdown) +12 34 56 78(input)
Default Prefixes
The component includes 24 common country code prefixes by default:
- US/Canada (+1), UK (+44), France (+33), Germany (+49), Italy (+39)
- Spain (+34), China (+86), India (+91), Japan (+81), South Korea (+82)
- Russia (+7), Australia (+61), Brazil (+55), Mexico (+52), Netherlands (+31)
- Sweden (+46), Switzerland (+41), Belgium (+32), Poland (+48), Portugal (+351)
- Greece (+30), Norway (+47), Denmark (+45), Finland (+358)
You can override these by providing a custom prefixes array.
Value Structure
The component returns a rich PhoneValue object with multiple formats:
full: Complete formatted number for displayraw: Digits only (no formatting) - use this for API submissionsprefix(split mode): Selected country codenumber(split mode): Formatted number without prefix
This structure provides flexibility for different use cases while maintaining a clean API.
Validation States
The component supports three validation states via the validationStatus prop:
"default": Normal state with light gray border"error": Red border with cross icon, displayserrorMessageif provided"success": Green border with check icon
The status icons appear on the right side of the input (or on the number input in split mode).
Form Control Integration
PhoneField wraps the input in a FormControl component that provides consistent styling for labels, descriptions, error messages, and validation states across all form fields.
Accessibility
- The input uses
type="tel"which triggers numeric keyboards on mobile devices - The component automatically associates the label with the input using the
idattribute - In split mode, both the prefix dropdown and number input have appropriate
aria-labelattributes - Disabled fields have appropriate visual feedback with reduced opacity
- Required fields are indicated with a red asterisk (unless
hideRequiredStaris true) - Status icons (check/cross) provide visual validation feedback
Cursor Position Management
The component intelligently manages cursor position during formatting. When you type digits and the component adds spaces or special characters, the cursor stays at the correct position relative to the digits you entered, not jumping to the end of the input.
Client Component
This component uses React hooks (useState, useEffect, useRef, useId) and must be used in a client component. Add "use client" at the top of files that use this component.
Examples
US Phone Number
<PhoneField
label="Phone Number"
mode="full"
pattern="+1 (###) ###-####"
placeholder="Enter US phone number"
/>French Phone Number
<PhoneField
label="Numéro de téléphone"
mode="full"
pattern="+33 ## ## ## ## ##"
placeholder="Entrez votre numéro"
/>International with Custom Prefixes
<PhoneField
label="Contact Number"
mode="split"
prefixes={[
{ code: '+1', label: 'US/Canada (+1)', country: 'US' },
{ code: '+33', label: 'France (+33)', country: 'FR' },
{ code: '+44', label: 'UK (+44)', country: 'GB' },
]}
defaultPrefix="+33"
pattern="## ## ## ## ##"
/>With Validation
<PhoneField
label="Phone Number"
mode="full"
pattern="## ## ## ## ##"
validationStatus="error"
errorMessage="Please enter a valid phone number"
/>With Icon
<PhoneField
label="Phone Number"
mode="full"
pattern="## ## ## ## ##"
icon="CalendarDefault"
/>On This Page
InstallationUsageFull ModeSplit ModeAPI ReferencePhoneFieldPhoneValue TypePhonePrefix TypeNotesPattern FormattingMode ComparisonDefault PrefixesValue StructureValidation StatesForm Control IntegrationAccessibilityCursor Position ManagementClient ComponentExamplesUS Phone NumberFrench Phone NumberInternational with Custom PrefixesWith ValidationWith Icon