Phone Input
A phone number input with country selection, formatting, and validation.
phone-input-demo.tsx
"use client"
import { useState } from "react"
import { PhoneInput } from "@/components/ui/phone-input"
export function PhoneInputDemo() {
const [phoneNumber, setPhoneNumber] = useState<string>()
return (
<div className="w-[300px]">
<PhoneInput
value={phoneNumber}
onChange={setPhoneNumber}
placeholder="Enter a phone number"
/>
</div>
)
}About
This component was inspired by the Phone Input component created by @omeralpi_.
Installation
npx shadcn@latest add @9ui/phone-input
Usage
Imports
import { PhoneInput } from "@/components/ui/phone-input"Anatomy
<PhoneInput />Examples
Default Country
phone-input-default-country-demo.tsx
"use client"
import { useState } from "react"
import { PhoneInput } from "@/components/ui/phone-input"
export function PhoneInputDefaultCountryDemo() {
const [phoneNumber, setPhoneNumber] = useState<string>()
return (
<div className="w-[300px]">
<PhoneInput
value={phoneNumber}
onChange={setPhoneNumber}
defaultCountry="TR"
placeholder="Enter a phone number"
/>
</div>
)
}Internationalization Format
phone-input-internationalization-demo.tsx
"use client"
import { useState } from "react"
import tr from "react-phone-number-input/locale/tr"
import { PhoneInput } from "@/components/ui/phone-input"
export function PhoneInputInternationalizationDemo() {
const [phoneNumber, setPhoneNumber] = useState<string>()
return (
<div className="w-[300px]">
<PhoneInput
value={phoneNumber}
onChange={setPhoneNumber}
labels={tr}
defaultCountry="TR"
placeholder="Telefon numarası"
/>
</div>
)
}Force International Format
phone-input-international-demo.tsx
"use client"
import { useState } from "react"
import { PhoneInput } from "@/components/ui/phone-input"
export function PhoneInputInternationalDemo() {
const [phoneNumber, setPhoneNumber] = useState<string>()
return (
<div className="w-[300px]">
<PhoneInput
value={phoneNumber}
onChange={setPhoneNumber}
international
defaultCountry="TR"
placeholder="Enter a phone number"
/>
</div>
)
}Force National Format
phone-input-national-demo.tsx
"use client"
import { useState } from "react"
import { PhoneInput } from "@/components/ui/phone-input"
export function PhoneInputNationalDemo() {
const [phoneNumber, setPhoneNumber] = useState<string>()
return (
<div className="w-[300px]">
<PhoneInput
value={phoneNumber}
onChange={setPhoneNumber}
international={false}
defaultCountry="TR"
placeholder="Enter a phone number"
/>
</div>
)
}Initial Value Format
phone-input-initial-value-format-demo.tsx
"use client"
import { useState } from "react"
import { PhoneInput } from "@/components/ui/phone-input"
export function PhoneInputInitialValueFormatDemo() {
const [phoneNumber, setPhoneNumber] = useState<string>()
return (
<div className="w-[300px]">
<PhoneInput
value={phoneNumber}
onChange={setPhoneNumber}
initialValueFormat="national"
placeholder="Enter a phone number"
/>
</div>
)
}Formatting Value
National: Enter a phone number
International: Enter a phone number
Country code:
phone-input-formatting-value-demo.tsx
"use client"
import { useState } from "react"
import {
Country,
formatPhoneNumber,
formatPhoneNumberIntl,
getCountryCallingCode,
} from "react-phone-number-input"
import { PhoneInput } from "@/components/ui/phone-input"
export function PhoneInputFormattingValueDemo() {
const [phoneNumber, setPhoneNumber] = useState<string>()
const [country, setCountry] = useState<Country>()
return (
<div className="w-[300px] space-y-4">
<PhoneInput
value={phoneNumber}
onChange={setPhoneNumber}
onCountryChange={setCountry}
placeholder="Enter a phone number"
/>
<div className="space-y-2 text-sm">
<div>
<span className="font-semibold">National:</span>{" "}
{phoneNumber ? (
formatPhoneNumber(phoneNumber)
) : (
<span className="text-muted-foreground">Enter a phone number</span>
)}
</div>
<div>
<span className="font-semibold">International:</span>{" "}
{phoneNumber ? (
formatPhoneNumberIntl(phoneNumber)
) : (
<span className="text-muted-foreground">Enter a phone number</span>
)}
</div>
<div>
<span className="font-semibold">Country code:</span>{" "}
{country && getCountryCallingCode(country)}
</div>
</div>
</div>
)
}With Validation
phone-input-with-validation-demo.tsx
"use client"
import { useState } from "react"
import { isValidPhoneNumber } from "react-phone-number-input"
import { PhoneInput } from "@/components/ui/phone-input"
export function PhoneInputWithValidationDemo() {
const [phoneNumber, setPhoneNumber] = useState<string>()
const [error, setError] = useState<string>()
const handleChange = (value: string | undefined) => {
setPhoneNumber(value)
if (value && !isValidPhoneNumber(value)) {
setError("Please enter a valid phone number")
} else {
setError(undefined)
}
}
return (
<div className="w-[300px] space-y-2">
<PhoneInput
value={phoneNumber}
onChange={handleChange}
placeholder="Enter a phone number"
aria-invalid={!!error}
/>
{error && <p className="text-destructive text-sm">{error}</p>}
{phoneNumber && !error && (
<p className="text-muted-foreground text-sm">
Valid phone number entered
</p>
)}
</div>
)
}Form
phone-input-form-demo.tsx
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { isValidPhoneNumber } from "react-phone-number-input"
import { z } from "zod"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { PhoneInput } from "@/components/ui/phone-input"
const schema = z.object({
phoneNumber: z.string().refine(isValidPhoneNumber, "Invalid phone number"),
})
type FormValues = z.infer<typeof schema>
export function PhoneInputFormDemo() {
const form = useForm<FormValues>({
resolver: zodResolver(schema),
defaultValues: {
phoneNumber: "",
},
})
const onSubmit = (data: FormValues) => {
console.log(data)
}
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="flex w-[300px] flex-col gap-4"
>
<FormField
name="phoneNumber"
control={form.control}
render={({ field, fieldState }) => (
<FormItem>
<FormLabel>Phone Number</FormLabel>
<FormControl>
<PhoneInput
{...field}
placeholder="Enter a phone number"
aria-invalid={!!fieldState.error}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}