DEV Community

ApogeoAPI
ApogeoAPI

Posted on • Originally published at apogeoapi.com

How to Build a Phone + Flag Selector in React (with Country Codes)

Phone number inputs with country codes are one of those deceptively tricky UI components. Here's how to build a polished one with flag images and dial codes using ApogeoAPI.

What We're Building

A phone number input that:

  • Shows a flag + country dial code in a dropdown
  • Lets the user search countries by name
  • Combines the dial code with the number the user types
  • Is fully TypeScript-typed

Step 1: Fetch Countries with Phone Codes

The ApogeoAPI countries endpoint returns phone_code and flag_url for every country.

// types.ts
export interface Country {
iso2: string;
name: string;
phone_code: string;
flag_url: string;
}
// useCountries.ts
import { useEffect, useState } from 'react';
export function useCountries() {
const [countries, setCountries] = useState([]);
useEffect(() => {
fetch('https://api.apogeoapi.com/v1/countries', {
headers: { 'X-API-Key': process.env.NEXT_PUBLIC_APOGEO_KEY! },
})
.then(r => r.json())
.then(setCountries);
}, []);
return countries;
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Build the PhoneInput Component

'use client';
import { useState, useMemo } from 'react';
import { useCountries } from './useCountries';
interface Props {
value: string;
onChange: (fullNumber: string) => void;
}
export function PhoneInput({ value, onChange }: Props) {
const countries = useCountries();
const [selectedIso, setSelectedIso] = useState('US');
const [number, setNumber] = useState('');
const [search, setSearch] = useState('');
const [open, setOpen] = useState(false);
const selected = countries.find(c => c.iso2 === selectedIso);
const filtered = useMemo(() =>
countries.filter(c =>
c.name.toLowerCase().includes(search.toLowerCase())
), [countries, search]);
const handleSelect = (country: Country) => {
setSelectedIso(country.iso2);
setOpen(false);
setSearch('');
onChange(`+${country.phone_code}${number}`);
};
const handleNumber = (e: React.ChangeEvent) => {
setNumber(e.target.value);
onChange(`+${selected?.phone_code ?? ''}${e.target.value}`);
};
return (

{/* Dial code selector */}
 setOpen(!open)}
className="flex items-center gap-1 border rounded px-3 py-2 min-w-[90px]">
{selected && (
<>

+{selected.phone_code}

)}

{/* Dropdown */}
{open && (

 setSearch(e.target.value)}
className="w-full px-3 py-2 border-b text-sm outline-none" />

{filtered.map(c => (
 handleSelect(c)}
className="flex items-center gap-2 px-3 py-2 hover:bg-gray-50 cursor-pointer text-sm">

{c.name}
+{c.phone_code}

))}

)}
{/* Number input */}

);
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Handle the Combined Value

The onChange callback fires with the full international number: +1 4155552671. Store it in your form state and send it as-is to your backend.

Optimizations

  • Cache the country list: Countries change rarely. Use SWR with a 24-hour stale time or cache in localStorage.
  • Default to user's country: Combine with the IP geolocation endpoint — GET /v1/geolocate/auto — to pre-select the right dial code automatically.
  • Memoize filtered list: The useMemo above ensures filtering is fast even with 250 countries.

Originally published at https://apogeoapi.com/blog/phone-flag-selector-react. Try ApogeoAPI free at apogeoapi.com.

Top comments (0)