Telephone number input React component with country selection, validation, and formatting capabilities
—
Extensive customization options for tailoring React Phone Number Input components to specific design requirements. The library provides comprehensive styling systems, custom component integration, and flexible configuration options.
Complete CSS styling system with custom properties and modifier classes.
/**
* CSS stylesheet import for default styling
* Required for components with country selection dropdown
*/
import "react-phone-number-input/style.css";
/**
* CSS class structure and modifier classes
*/
interface CSSClasses {
/** Base component class */
".PhoneInput": CSSProperties;
/** Focus state modifier */
".PhoneInput--focus": CSSProperties;
/** Disabled state modifier */
".PhoneInput--disabled": CSSProperties;
/** Read-only state modifier */
".PhoneInput--readOnly": CSSProperties;
/** Country select dropdown */
".PhoneInputCountrySelect": CSSProperties;
/** Country select arrow */
".PhoneInputCountrySelectArrow": CSSProperties;
/** Country flag display */
".PhoneInputCountryFlag": CSSProperties;
/** Phone number input field */
".PhoneInputInput": CSSProperties;
}Basic Styling Usage:
import React, { useState } from "react";
import PhoneInput from "react-phone-number-input";
import "react-phone-number-input/style.css";
import "./custom-phone-input.css"; // Additional custom styles
function StyledExample() {
const [value, setValue] = useState();
return (
<PhoneInput
value={value}
onChange={setValue}
className="my-phone-input"
style={{
border: '2px solid #007bff',
borderRadius: '8px'
}}
/>
);
}Extensive CSS custom properties for fine-tuned styling control.
/**
* Available CSS custom properties for customization
*/
interface CSSCustomProperties {
/** Country flag icon height */
"--PhoneInputCountryFlag-height"?: string;
/** Country flag icon border color */
"--PhoneInputCountryFlag-borderColor"?: string;
/** Country select arrow color */
"--PhoneInputCountrySelectArrow-color"?: string;
/** Country select arrow opacity (unfocused) */
"--PhoneInputCountrySelectArrow-opacity"?: string;
/** Focus state outline color */
"--PhoneInput-color--focus"?: string;
/** Component border color */
"--PhoneInput-borderColor"?: string;
/** Component background color */
"--PhoneInput-backgroundColor"?: string;
/** Input text color */
"--PhoneInput-color"?: string;
/** Placeholder text color */
"--PhoneInput-placeholderColor"?: string;
/** Component font size */
"--PhoneInput-fontSize"?: string;
/** Component padding */
"--PhoneInput-padding"?: string;
/** Component border radius */
"--PhoneInput-borderRadius"?: string;
}CSS Custom Properties Example:
/* Custom CSS file */
.custom-phone-input {
--PhoneInputCountryFlag-height: 1.2em;
--PhoneInputCountryFlag-borderColor: #ddd;
--PhoneInputCountrySelectArrow-color: #007bff;
--PhoneInputCountrySelectArrow-opacity: 0.8;
--PhoneInput-color--focus: #007bff;
--PhoneInput-borderColor: #ced4da;
--PhoneInput-backgroundColor: #fff;
--PhoneInput-color: #495057;
--PhoneInput-fontSize: 1rem;
--PhoneInput-padding: 0.75rem;
--PhoneInput-borderRadius: 0.375rem;
}
.custom-phone-input:hover {
--PhoneInput-borderColor: #007bff;
}
.custom-phone-input.PhoneInput--focus {
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}Integration of custom input components with full prop forwarding.
/**
* Custom input component interface
* Must use React.forwardRef() to forward ref to underlying input element
*/
interface CustomInputProps {
/** Current input value */
value: string;
/** Input change handler */
onChange(event: React.ChangeEvent<HTMLInputElement>): void;
/** Keyboard event handler for special key handling */
onKeyDown?(event: React.KeyboardEvent<HTMLInputElement>): void;
/** Paste event handler for formatting pasted content */
onPaste?(event: React.ClipboardEvent<HTMLInputElement>): void;
/** Standard input properties */
placeholder?: string;
disabled?: boolean;
readOnly?: boolean;
autoComplete?: string;
className?: string;
style?: React.CSSProperties;
onFocus?(event: React.FocusEvent<HTMLInputElement>): void;
onBlur?(event: React.FocusEvent<HTMLInputElement>): void;
// ... any other input props
}
type CustomInputComponent = React.ForwardRefExoticComponent<CustomInputProps & React.RefAttributes<HTMLInputElement>>;
interface InputComponentProps {
/** Custom input component */
inputComponent?: CustomInputComponent;
/** Additional props to pass to input component */
numberInputProps?: object;
}Custom Input Examples:
import React, { forwardRef } from "react";
import PhoneInput from "react-phone-number-input";
// Material-UI styled input
const MaterialInput = forwardRef<HTMLInputElement, any>((props, ref) => (
<input
{...props}
ref={ref}
className={`material-input ${props.className || ""}`}
style={{
border: 'none',
borderBottom: '2px solid #ddd',
borderRadius: 0,
padding: '8px 0',
fontSize: '16px',
backgroundColor: 'transparent',
outline: 'none',
...props.style
}}
/>
));
// Bootstrap styled input
const BootstrapInput = forwardRef<HTMLInputElement, any>((props, ref) => (
<input
{...props}
ref={ref}
className={`form-control ${props.className || ""}`}
/>
));
// Usage examples
function CustomInputExample() {
const [value, setValue] = useState();
return (
<PhoneInput
inputComponent={MaterialInput}
value={value}
onChange={setValue}
numberInputProps={{
style: { borderBottomColor: '#007bff' }
}}
/>
);
}
function BootstrapExample() {
const [value, setValue] = useState();
return (
<PhoneInput
inputComponent={BootstrapInput}
value={value}
onChange={setValue}
className="mb-3"
/>
);
}Replace the default country selection dropdown with custom implementations.
/**
* Custom country select component interface
*/
interface CountrySelectProps {
/** Currently selected country */
value?: Country;
/** Country selection change handler */
onChange(country?: Country): void;
/** Available countries list */
options: Array<{
value?: Country;
label: string;
divider?: boolean;
}>;
/** Country labels for display */
labels: Labels;
/** Disabled state */
disabled?: boolean;
/** Read-only state */
readOnly?: boolean;
/** Custom styling */
className?: string;
style?: React.CSSProperties;
/** Accessibility properties */
"aria-label"?: string;
tabIndex?: number;
}
interface CountrySelectComponentProps {
/** Custom country select component */
countrySelectComponent?: React.ElementType;
/** Props to pass to country select component */
countrySelectProps?: object;
}Custom Country Select Examples:
import React from "react";
import PhoneInput from "react-phone-number-input";
// Custom dropdown with search
function SearchableCountrySelect({ value, onChange, options, labels, ...rest }) {
const [searchTerm, setSearchTerm] = useState("");
const [isOpen, setIsOpen] = useState(false);
const filteredOptions = options.filter(option =>
!searchTerm || labels[option.value]?.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div className="custom-country-select" {...rest}>
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
className="country-select-button"
>
{value ? labels[value] : "Select Country"}
</button>
{isOpen && (
<div className="country-dropdown">
<input
type="text"
placeholder="Search countries..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="country-search"
/>
<div className="country-options">
{filteredOptions.map(option => (
<button
key={option.value || "international"}
type="button"
onClick={() => {
onChange(option.value);
setIsOpen(false);
}}
className="country-option"
>
{option.label}
</button>
))}
</div>
</div>
)}
</div>
);
}
// Usage with custom country select
function CustomCountrySelectExample() {
const [value, setValue] = useState();
return (
<PhoneInput
countrySelectComponent={SearchableCountrySelect}
countrySelectProps={{
className: "my-country-select"
}}
value={value}
onChange={setValue}
/>
);
}Customize country flag display with custom flag components or flag sources.
/**
* Flag component interfaces
*/
interface FlagProps {
/** Country code */
country: Country;
/** Country display name */
countryName: string;
/** Custom flag image URL */
flagUrl?: string;
/** Custom flag components map */
flags?: Flags;
/** Additional styling */
className?: string;
style?: React.CSSProperties;
}
interface EmbeddedFlagProps {
/** Accessibility title */
title: string;
/** Additional styling */
className?: string;
style?: React.CSSProperties;
}
type Flag = (props: FlagProps) => JSX.Element;
type EmbeddedFlag = (props: EmbeddedFlagProps) => JSX.Element;
type Flags = Partial<Record<Country, EmbeddedFlag>>;
interface FlagCustomizationProps {
/** Custom flag component renderer */
flagComponent?: Flag;
/** Base URL for flag images */
flagUrl?: string;
/** Custom embedded flag components */
flags?: Flags;
}Custom Flag Examples:
import React from "react";
import PhoneInput from "react-phone-number-input";
import flags from "react-phone-number-input/flags";
// Custom flag component with emoji
function EmojiFlag({ country, countryName, ...rest }: FlagProps) {
const flagEmoji = getFlagEmoji(country); // Your emoji lookup function
return (
<span
{...rest}
role="img"
aria-label={countryName}
className="emoji-flag"
>
{flagEmoji}
</span>
);
}
// Custom flag with image CDN
function ImageFlag({ country, countryName, ...rest }: FlagProps) {
return (
<img
{...rest}
src={`https://flagcdn.com/24x18/${country.toLowerCase()}.png`}
alt={countryName}
className="flag-image"
style={{ width: '24px', height: '18px' }}
/>
);
}
// Usage examples
function EmojiFlexExample() {
const [value, setValue] = useState();
return (
<PhoneInput
flagComponent={EmojiFlag}
value={value}
onChange={setValue}
/>
);
}
function CustomFlagUrlExample() {
const [value, setValue] = useState();
return (
<PhoneInput
flagUrl="https://flagpedia.net/data/flags/icon/72x54/"
value={value}
onChange={setValue}
/>
);
}
function MixedFlagsExample() {
const [value, setValue] = useState();
const customFlags = {
...flags,
US: ({ title, ...rest }) => <span {...rest}>🇺🇸</span>,
CA: ({ title, ...rest }) => <span {...rest}>🇨🇦</span>
};
return (
<PhoneInput
flags={customFlags}
value={value}
onChange={setValue}
/>
);
}Customize the overall component layout and container structure.
/**
* Container customization props
*/
interface ContainerCustomizationProps {
/** Custom container component */
containerComponent?: React.ElementType;
/** Props to pass to container component */
containerComponentProps?: object;
/** Component layout style */
style?: React.CSSProperties;
/** Component CSS class */
className?: string;
}
// Default container structure equivalent
function DefaultContainer({ className, style, children, ...rest }) {
return (
<div
className={`PhoneInput ${className || ""}`}
style={style}
{...rest}
>
{children}
</div>
);
}Container Customization Examples:
// Grid-based layout container
function GridContainer({ children, className, ...rest }) {
return (
<div
className={`phone-input-grid ${className || ""}`}
style={{
display: 'grid',
gridTemplateColumns: 'auto 1fr',
gap: '8px',
alignItems: 'center'
}}
{...rest}
>
{children}
</div>
);
}
// Flexbox layout container
function FlexContainer({ children, className, ...rest }) {
return (
<div
className={`phone-input-flex ${className || ""}`}
style={{
display: 'flex',
alignItems: 'stretch',
border: '1px solid #ddd',
borderRadius: '4px',
overflow: 'hidden'
}}
{...rest}
>
{children}
</div>
);
}
// Usage examples
function CustomContainerExample() {
const [value, setValue] = useState();
return (
<PhoneInput
containerComponent={GridContainer}
containerComponentProps={{
className: "my-grid-container"
}}
value={value}
onChange={setValue}
/>
);
}Integration patterns for popular styling frameworks and design systems.
// Theme integration interfaces
interface ThemeProps {
theme?: {
colors?: {
primary?: string;
border?: string;
background?: string;
text?: string;
focus?: string;
};
spacing?: {
sm?: string;
md?: string;
lg?: string;
};
borderRadius?: string;
fontSize?: string;
};
}Theme Integration Examples:
// Styled Components theme integration
import styled from "styled-components";
import PhoneInput from "react-phone-number-input";
const ThemedPhoneInput = styled(PhoneInput)`
--PhoneInput-color--focus: ${props => props.theme.colors.primary};
--PhoneInput-borderColor: ${props => props.theme.colors.border};
--PhoneInput-backgroundColor: ${props => props.theme.colors.background};
--PhoneInput-color: ${props => props.theme.colors.text};
--PhoneInput-fontSize: ${props => props.theme.fontSize};
--PhoneInput-borderRadius: ${props => props.theme.borderRadius};
--PhoneInput-padding: ${props => props.theme.spacing.md};
`;
// Tailwind CSS integration
function TailwindPhoneInput(props) {
return (
<div className="relative">
<PhoneInput
{...props}
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
style={{
"--PhoneInput-color--focus": "#3b82f6",
"--PhoneInputCountryFlag-height": "1.25rem"
}}
/>
</div>
);
}
// CSS-in-JS integration
const phoneInputStyles = {
base: {
"--PhoneInput-borderColor": "#e5e7eb",
"--PhoneInput-backgroundColor": "#ffffff",
"--PhoneInput-color": "#374151",
"--PhoneInput-fontSize": "0.875rem",
"--PhoneInput-padding": "0.5rem 0.75rem",
"--PhoneInput-borderRadius": "0.375rem"
},
focus: {
"--PhoneInput-color--focus": "#3b82f6"
},
error: {
"--PhoneInput-borderColor": "#ef4444"
}
};
function CSSInJSExample({ error, ...props }) {
const styles = {
...phoneInputStyles.base,
...phoneInputStyles.focus,
...(error && phoneInputStyles.error)
};
return (
<PhoneInput
{...props}
style={styles}
/>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-phone-number-input