PhoneInput
A formatted phone number input with international support
A specialized phone number input component with automatic formatting, country selection, and international support. Perfect for collecting phone numbers in forms.
Overview
PhoneInput handles phone number formatting automatically and provides a country picker for international support. It manages validation, formatting, and provides a clean UX for phone entry.
Use it for:
- User registration and signup forms
- Contact information collection
- Call or SMS features
- Appointment booking forms
- Two-factor authentication
- Profile updates
Basic Usage
import { PhoneInput } from 'prizmux'
import { useState } from 'react'
import { View } from 'react-native'
export default function SignUpForm() {
const [phoneData, setPhoneData] = useState(null)
return (
<View style={{ padding: 16 }}>
<PhoneInput
onChange={(value) => setPhoneData(value)}
placeholder="712 345 678"
/>
</View>
)
}
import { PhoneInput } from 'prizmux'
import { useState } from 'react'
import { View } from 'react-native'
export default function SignUpForm() {
const [phoneData, setPhoneData] = useState(null)
return (
<View style={{ padding: 16 }}>
<PhoneInput
onChange={(value) => setPhoneData(value)}
placeholder="712 345 678"
/>
</View>
)
}
With Default Country
import { PhoneInput } from 'prizmux'
<PhoneInput
defaultCountryCode="GB"
onChange={(value) => console.log(value)}
placeholder="7700 900000"
/>
import { PhoneInput } from 'prizmux'
<PhoneInput
defaultCountryCode="GB"
onChange={(value) => console.log(value)}
placeholder="7700 900000"
/>
Restricted Countries
import { PhoneInput } from 'prizmux'
<PhoneInput
defaultCountryCode="UG"
allowedCountries={['UG', 'KE', 'TZ', 'RW']}
onChange={(value) => console.log(value)}
/>
import { PhoneInput } from 'prizmux'
<PhoneInput
defaultCountryCode="UG"
allowedCountries={['UG', 'KE', 'TZ', 'RW']}
onChange={(value) => console.log(value)}
/>
With Label and Error
import { PhoneInput } from 'prizmux'
import { useState } from 'react'
import { View, Text } from 'react-native'
export default function ContactForm() {
const [phoneData, setPhoneData] = useState(null)
const [error, setError] = useState('')
const handlePhoneChange = (value) => {
setPhoneData(value)
// Validate
if (value.full && value.full.length < 10) {
setError('Phone number too short')
} else {
setError('')
}
}
return (
<View style={{ gap: 12 }}>
<Text style={{ fontSize: 16, fontWeight: '600' }}>Phone Number</Text>
<PhoneInput
value={phoneData}
onChange={handlePhoneChange}
label="Mobile Phone"
error={error}
placeholder="Enter phone number"
/>
{error && <Text style={{ color: '#EF4444' }}>{error}</Text>}
</View>
)
}
import { PhoneInput } from 'prizmux'
import { useState } from 'react'
import { View, Text } from 'react-native'
export default function ContactForm() {
const [phoneData, setPhoneData] = useState(null)
const [error, setError] = useState('')
const handlePhoneChange = (value) => {
setPhoneData(value)
// Validate
if (value.full && value.full.length < 10) {
setError('Phone number too short')
} else {
setError('')
}
}
return (
<View style={{ gap: 12 }}>
<Text style={{ fontSize: 16, fontWeight: '600' }}>Phone Number</Text>
<PhoneInput
value={phoneData}
onChange={handlePhoneChange}
label="Mobile Phone"
error={error}
placeholder="Enter phone number"
/>
{error && <Text style={{ color: '#EF4444' }}>{error}</Text>}
</View>
)
}
Form Integration
import { PhoneInput, Button } from 'prizmux'
import { useState } from 'react'
import { View, Text } from 'react-native'
export default function RegistrationForm() {
const [phoneData, setPhoneData] = useState(null)
const [loading, setLoading] = useState(false)
const handleSubmit = async () => {
if (!phoneData?.full) {
alert('Please enter a phone number')
return
}
setLoading(true)
try {
await submitPhoneNumber(phoneData.full)
alert('Phone number submitted!')
} catch (error) {
alert('Error: ' + error.message)
} finally {
setLoading(false)
}
}
return (
<View style={{ gap: 16, padding: 16 }}>
<PhoneInput
value={phoneData}
onChange={setPhoneData}
placeholder="Enter your phone number"
/>
<Button
title="Verify Phone"
isLoading={loading}
fullWidth={true}
onPress={handleSubmit}
/>
</View>
)
}
import { PhoneInput, Button } from 'prizmux'
import { useState } from 'react'
import { View, Text } from 'react-native'
export default function RegistrationForm() {
const [phoneData, setPhoneData] = useState(null)
const [loading, setLoading] = useState(false)
const handleSubmit = async () => {
if (!phoneData?.full) {
alert('Please enter a phone number')
return
}
setLoading(true)
try {
await submitPhoneNumber(phoneData.full)
alert('Phone number submitted!')
} catch (error) {
alert('Error: ' + error.message)
} finally {
setLoading(false)
}
}
return (
<View style={{ gap: 16, padding: 16 }}>
<PhoneInput
value={phoneData}
onChange={setPhoneData}
placeholder="Enter your phone number"
/>
<Button
title="Verify Phone"
isLoading={loading}
fullWidth={true}
onPress={handleSubmit}
/>
</View>
)
}
Accessing Phone Data
The onChange callback receives a PhoneInputValue object with three properties:
interface PhoneInputValue {
country: Country // Country object with code and dial info
number: string // Local number without dial code
full: string // Full number with dial code (e.g. "+254712345678")
}
// Usage
<PhoneInput
onChange={(value) => {
console.log(value.country.code) // 'UG'
console.log(value.number) // '712345678'
console.log(value.full) // '+256712345678'
}}
/>
interface PhoneInputValue {
country: Country // Country object with code and dial info
number: string // Local number without dial code
full: string // Full number with dial code (e.g. "+254712345678")
}
// Usage
<PhoneInput
onChange={(value) => {
console.log(value.country.code) // 'UG'
console.log(value.number) // '712345678'
console.log(value.full) // '+256712345678'
}}
/>
Custom Flag Rendering
import { PhoneInput } from 'prizmux'
<PhoneInput
onChange={(value) => {}}
renderFlag={(country) => (
<Text style={{ fontSize: 24, marginRight: 8 }}>
{country.code === 'US' ? '🇺🇸' : '🌍'}
</Text>
)}
/>
import { PhoneInput } from 'prizmux'
<PhoneInput
onChange={(value) => {}}
renderFlag={(country) => (
<Text style={{ fontSize: 24, marginRight: 8 }}>
{country.code === 'US' ? '🇺🇸' : '🌍'}
</Text>
)}
/>
Props Reference
| Prop | Type | Default | Description |
|---|---|---|---|
value | PhoneInputValue | - | Current phone data |
onChange | (value: PhoneInputValue) => void | Required | Called when value changes |
defaultCountryCode | string | 'US' | Initial country code |
allowedCountries | string[] | - | Restrict to specific countries |
placeholder | string | '712 345 678' | Input placeholder |
label | string | - | Input label |
error | string | - | Error message |
disabled | boolean | false | Disable input |
pickerTitle | string | 'Select Country' | Country picker title |
searchPlaceholder | string | - | Country search placeholder |
dropdownIcon | ReactNode | - | Custom dropdown icon |
renderFlag | (country) => ReactNode | - | Custom flag rendering |
containerStyle | ViewStyle | - | Container styles |
inputStyle | TextStyle | - | Input field styles |
labelStyle | TextStyle | - | Label text styles |
errorStyle | TextStyle | - | Error text styles |
Features
- 🌍 International country support
- 🎯 Automatic formatting and detection
- 🔍 Country search and filtering
- 🎨 Customizable styles and icons
- ✅ Full TypeScript support
- ♿ Accessibility support
Usage
Basic Phone Input
import { PhoneInput } from 'prizmux'
import { useState } from 'react'
export default function SignUp() {
const [phone, setPhone] = useState('')
return (
<PhoneInput
value={phone}
onChange={setPhone}
placeholder="Enter phone number"
/>
)
}
import { PhoneInput } from 'prizmux'
import { useState } from 'react'
export default function SignUp() {
const [phone, setPhone] = useState('')
return (
<PhoneInput
value={phone}
onChange={setPhone}
placeholder="Enter phone number"
/>
)
}
With Default Country
<PhoneInput
value={phone}
onChange={setPhone}
defaultCountry="GB"
placeholder="+44"
/>
<PhoneInput
value={phone}
onChange={setPhone}
defaultCountry="GB"
placeholder="+44"
/>
With Country Change Handling
import { PhoneInput } from 'prizmux'
import { useState } from 'react'
export default function ContactForm() {
const [phone, setPhone] = useState('')
const [country, setCountry] = useState('US')
return (
<>
<PhoneInput
value={phone}
onChange={setPhone}
defaultCountry={country}
onCountryChange={setCountry}
/>
<Text>Country Code: {country}</Text>
</>
)
}
import { PhoneInput } from 'prizmux'
import { useState } from 'react'
export default function ContactForm() {
const [phone, setPhone] = useState('')
const [country, setCountry] = useState('US')
return (
<>
<PhoneInput
value={phone}
onChange={setPhone}
defaultCountry={country}
onCountryChange={setCountry}
/>
<Text>Country Code: {country}</Text>
</>
)
}
Form Integration
import { PhoneInput, Button, Text } from 'prizmux'
import { useState } from 'react'
import { View } from 'react-native'
export default function RegistrationForm() {
const [phone, setPhone] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const validatePhone = (value) => {
if (!value) {
setError('Phone number is required')
return false
}
if (value.length < 10) {
setError('Phone number is too short')
return false
}
setError('')
return true
}
const handleSubmit = async () => {
if (!validatePhone(phone)) return
setLoading(true)
try {
await registerPhone(phone)
// Success
} catch (err) {
setError(err.message)
} finally {
setLoading(false)
}
}
return (
<View style={{ gap: 16 }}>
<PhoneInput
value={phone}
onChange={setPhone}
error={error}
placeholder="Phone number"
/>
{error && <Text style={{ color: 'red' }}>{error}</Text>}
<Button
title="Verify"
loading={loading}
onPress={handleSubmit}
/>
</View>
)
}
import { PhoneInput, Button, Text } from 'prizmux'
import { useState } from 'react'
import { View } from 'react-native'
export default function RegistrationForm() {
const [phone, setPhone] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const validatePhone = (value) => {
if (!value) {
setError('Phone number is required')
return false
}
if (value.length < 10) {
setError('Phone number is too short')
return false
}
setError('')
return true
}
const handleSubmit = async () => {
if (!validatePhone(phone)) return
setLoading(true)
try {
await registerPhone(phone)
// Success
} catch (err) {
setError(err.message)
} finally {
setLoading(false)
}
}
return (
<View style={{ gap: 16 }}>
<PhoneInput
value={phone}
onChange={setPhone}
error={error}
placeholder="Phone number"
/>
{error && <Text style={{ color: 'red' }}>{error}</Text>}
<Button
title="Verify"
loading={loading}
onPress={handleSubmit}
/>
</View>
)
}
Two-Factor Authentication
import { PhoneInput, Button, Text } from 'prizmux'
import { useState } from 'react'
import { View } from 'react-native'
export default function TwoFactorSetup() {
const [phone, setPhone] = useState('')
const [verified, setVerified] = useState(false)
const handleSendCode = async () => {
const result = await sendVerificationCode(phone)
if (result.success) {
setVerified(true)
// Navigate to code verification screen
}
}
return (
<View style={{ gap: 16 }}>
<Text variant="title">Add Phone Number</Text>
<Text variant="caption">
We'll send you a code to verify
</Text>
<PhoneInput
value={phone}
onChange={setPhone}
placeholder="Phone number"
disabled={verified}
/>
{!verified && (
<Button
title="Send Code"
onPress={handleSendCode}
/>
)}
</View>
)
}
import { PhoneInput, Button, Text } from 'prizmux'
import { useState } from 'react'
import { View } from 'react-native'
export default function TwoFactorSetup() {
const [phone, setPhone] = useState('')
const [verified, setVerified] = useState(false)
const handleSendCode = async () => {
const result = await sendVerificationCode(phone)
if (result.success) {
setVerified(true)
// Navigate to code verification screen
}
}
return (
<View style={{ gap: 16 }}>
<Text variant="title">Add Phone Number</Text>
<Text variant="caption">
We'll send you a code to verify
</Text>
<PhoneInput
value={phone}
onChange={setPhone}
placeholder="Phone number"
disabled={verified}
/>
{!verified && (
<Button
title="Send Code"
onPress={handleSendCode}
/>
)}
</View>
)
}
International Numbers
import { PhoneInput } from 'prizmux'
// Automatically formats based on country
<PhoneInput
value={phone}
onChange={setPhone}
defaultCountry="DE"
// Shows German format: +49 XXXX XXXX
/>
<PhoneInput
value={phone}
onChange={setPhone}
defaultCountry="JP"
// Shows Japanese format: +81 XX XXXX XXXX
/>
import { PhoneInput } from 'prizmux'
// Automatically formats based on country
<PhoneInput
value={phone}
onChange={setPhone}
defaultCountry="DE"
// Shows German format: +49 XXXX XXXX
/>
<PhoneInput
value={phone}
onChange={setPhone}
defaultCountry="JP"
// Shows Japanese format: +81 XX XXXX XXXX
/>
Formatting
PhoneInput automatically formats numbers based on selected country:
- US:
(123) 456-7890 - UK:
+44 1632 960291 - Germany:
+49 30 11001100 - France:
+33 1 42 68 53 00 - Others: International format maintained
Notes
- Automatically adds country code prefix
- Handles paste events with auto-formatting
- Validates format in real-time
- Supports all country codes
- Keyboard type set to phone-pad automatically
- Error state integrated for form validation
- International format compliance