Phone Input
A production-ready Indian phone number input with automatic formatting, real-time validation, WhatsApp verification toggle, and visual feedback. Perfect for authentication flows, contact forms, and user registration.
Installation
npx kesp-ui@latest add phone-inputOr specify a custom path: npx kesp-ui@latest add phone-input -p src/components/ui
Preview
InteractiveUsage
import PhoneInput from "@/components/ui/phone-input"
function RegistrationForm() {
const [phone, setPhone] = useState<string>("")
const handleSubmit = () => {
console.log("Phone number:", phone)
registerUser({ phone })
}
return (
<PhoneInput
value={phone}
onChange={setPhone}
/>
)
}import PhoneInput from "@/components/ui/phone-input"
function PhoneVerification() {
const [phone, setPhone] = useState<string>("")
const [useWhatsApp, setUseWhatsApp] = useState<boolean>(false)
const handleVerify = async () => {
const method = useWhatsApp ? "whatsapp" : "sms"
await sendOTP(phone, method)
}
return (
<div>
<PhoneInput
value={phone}
onChange={setPhone}
enableWhatsApp
whatsAppEnabled={useWhatsApp}
onWhatsAppToggle={setUseWhatsApp}
/>
<button onClick={handleVerify}>
Send OTP via {useWhatsApp ? "WhatsApp" : "SMS"}
</button>
</div>
)
}import PhoneInput from "@/components/ui/phone-input"
function SignupForm() {
const [phone, setPhone] = useState<string>("")
const [error, setError] = useState<string>("")
const validatePhone = async (value: string) => {
if (value.length !== 10) return
try {
const response = await fetch("/api/check-phone", {
method: "POST",
body: JSON.stringify({ phone: value })
})
const data = await response.json()
if (data.exists) {
setError("This phone number is already registered")
} else {
setError("")
}
} catch (err) {
setError("Unable to validate phone number")
}
}
return (
<PhoneInput
value={phone}
onChange={(value: string) => {
setPhone(value)
validatePhone(value)
}}
error={error}
/>
)
}import PhoneInput from "@/components/ui/phone-input"
function QuickContactForm() {
const phoneRef = useRef<HTMLInputElement>(null)
const handleSubmit = () => {
const phoneValue = phoneRef.current?.value
console.log("Phone:", phoneValue)
}
return (
<PhoneInput
ref={phoneRef}
placeholder="Enter your phone"
/>
)
}Key Features
Automatic Formatting
Automatically formats Indian phone numbers with proper spacing (XXXXX XXXXX). Handles paste operations and filters non-numeric characters seamlessly.
Real-Time Validation
Built-in validation for Indian phone numbers (must start with 6, 7, 8, or 9 and be exactly 10 digits). Shows clear error messages and visual feedback.
WhatsApp Verification
Optional WhatsApp toggle for alternative verification methods. Only enabled when phone number is valid. Includes animated toggle switch with clear visual states.
Visual Feedback
Dynamic border colors and icons show validation status. Green checkmark for valid numbers, red alert for errors, and blue highlight on focus. Smooth animations for state transitions.
Behavior Details
The phone input provides an intuitive user experience:
- Input Formatting: Numbers are automatically formatted as you type. The format is XXXXX XXXXX with a space after the 5th digit for readability.
- Validation Rules: Phone numbers must be exactly 10 digits and start with 6, 7, 8, or 9 (Indian mobile number format). Validation triggers on blur after user interaction.
- Paste Support: Pasting phone numbers works seamlessly. Non-numeric characters are automatically filtered out, and validation is triggered immediately.
- WhatsApp Toggle: The WhatsApp verification option only becomes available when the phone number is valid. Clicking toggles between SMS and WhatsApp verification methods.
The +91 country code prefix is fixed for Indian phone numbers and not editable by users
Props
| Prop | Type | Default | Description | Required |
|---|---|---|---|---|
| className | string | "" | Additional CSS classes on the wrapper | No |
| value | string | undefined | Controlled value (10 raw digits). Omit for uncontrolled mode | No |
| onChange | (value: string) => void | undefined | Called with raw 10-digit string on every change | No |
| enableWhatsApp | boolean | false | Show WhatsApp verification toggle below the input | No |
| whatsAppEnabled | boolean | undefined | Controlled WhatsApp toggle state | No |
| onWhatsAppToggle | (enabled: boolean) => void | undefined | Called when WhatsApp toggle is clicked | No |
| error | string | undefined | External error message to display | No |
| disabled | boolean | false | Disable the input and WhatsApp toggle | No |
| placeholder | string | "98765 43210" | Placeholder text for the input field | No |
Additional CSS classes on the wrapper
Controlled value (10 raw digits). Omit for uncontrolled mode
Called with raw 10-digit string on every change
Show WhatsApp verification toggle below the input
Controlled WhatsApp toggle state
Called when WhatsApp toggle is clicked
External error message to display
Disable the input and WhatsApp toggle
Placeholder text for the input field
Common Patterns
User Registration
Collect and validate phone numbers during user signup.
<PhoneInput
value={phone}
onChange={setPhone}
error={serverError}
placeholder="Enter your mobile number"
/>OTP Verification Flow
Combine with OTP input for complete phone verification.
const [phone, setPhone] = useState<string>("")
const [useWhatsApp, setUseWhatsApp] = useState<boolean>(false)
const [showOTP, setShowOTP] = useState<boolean>(false)
<PhoneInput
value={phone}
onChange={setPhone}
enableWhatsApp
whatsAppEnabled={useWhatsApp}
onWhatsAppToggle={setUseWhatsApp}
/>
<button
onClick={async () => {
await sendOTP(phone, useWhatsApp ? "whatsapp" : "sms")
setShowOTP(true)
}}
disabled={phone.length !== 10}
>
Send OTP
</button>
{showOTP && <OtpInput onComplete={verifyOTP} />}Contact Form
Use in contact forms with optional phone number collection.
interface FormData {
name: string
email: string
phone: string
}
const [formData, setFormData] = useState<FormData>({
name: "",
email: "",
phone: ""
})
<PhoneInput
value={formData.phone}
onChange={(phone: string) => setFormData({ ...formData, phone })}
placeholder="Phone (optional)"
/>Profile Update
Allow users to update their phone number with re-verification.
const [phone, setPhone] = useState<string>(user.phone)
const [hasChanged, setHasChanged] = useState<boolean>(false)
<PhoneInput
value={phone}
onChange={(value: string) => {
setPhone(value)
setHasChanged(value !== user.phone)
}}
/>
{hasChanged && phone.length === 10 && (
<button onClick={() => verifyNewPhone(phone)}>
Verify new number
</button>
)}CLI Reference
npx kesp-ui@latest add phone-inputnpx kesp-ui@latest add phone-input -p src/components/uinpx kesp-ui@latest listnpx kesp-ui --versionnpx kesp-ui --help