CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-phone-number-input

Telephone number input React component with country selection, validation, and formatting capabilities

Pending
Overview
Eval results
Files

customization.mddocs/

Customization and Styling

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.

Capabilities

CSS Styling System

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'
      }}
    />
  );
}

CSS Custom Properties

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);
}

Custom Input Components

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"
    />
  );
}

Custom Country Select Components

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}
    />
  );
}

Custom Flag Components

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}
    />
  );
}

Container and Layout Customization

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}
    />
  );
}

Theme Integration

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

docs

customization.md

framework-integrations.md

index.md

input-components.md

internationalization.md

phone-input-components.md

utility-functions.md

tile.json