A flexible, themeable React phone-number input built on top of react-phone-number-input.
npm install @jbenbrahim/phone-input- ✅ Works in any React app — zero CSS setup (styles are inline by default)
- ✅ Fully themeable:
sizepresets + granularthemetokens - ✅ Style escape hatches (
containerStyle/inputStyle) that always win - ✅ Full passthrough of the entire
react-phone-number-inputAPI (countries, labels, flags,international, native input attributes…) - ✅ Ref forwarding for focus management and form libraries
- ✅ Error state, disabled state, and clean focus tracking (with a focus ring)
- ✅ Optional inline
helperText/ error message - ✅ First-class TypeScript types
import { useState } from 'react';
import { PhoneInput } from '@jbenbrahim/phone-input';
function Example() {
const [phone, setPhone] = useState<string | undefined>();
return (
<PhoneInput
value={phone}
onChange={setPhone}
defaultCountry="US"
placeholder="Enter phone number"
/>
);
}
CustomPhoneInputis still exported (and remains the default export) for backward compatibility —PhoneInputis just a friendlier alias for the same component.
Two layers, resolved in order (last wins): size preset → theme tokens → inline style overrides.
<PhoneInput value={phone} onChange={setPhone} size="sm" /> {/* sm | md | lg */}<PhoneInput
value={phone}
onChange={setPhone}
theme={{
borderRadius: '9999px',
borderColorFocus: '#2563eb',
focusRing: '0 0 0 3px rgba(37, 99, 235, 0.2)',
background: '#f9fafb',
color: '#111827',
height: '3rem',
}}
/>| Token | Description |
|---|---|
height |
Control height |
fontSize |
Font size of the typed number |
paddingX |
Horizontal padding |
gap |
Space between the country select and the number input |
borderRadius |
Corner radius |
borderWidth |
Border thickness |
borderColor |
Resting border color |
borderColorFocus |
Border color while focused |
borderColorError |
Border color while isError is set |
background |
Control background |
color |
Text color |
focusRing |
Box-shadow applied on focus |
disabledOpacity |
Opacity while disabled |
containerStyle and inputStyle are merged last, so they beat every theme token:
<PhoneInput
value={phone}
onChange={setPhone}
containerStyle={{ border: '2px dashed hotpink' }}
inputStyle={{ fontWeight: 600 }}
/><PhoneInput
value={phone}
onChange={setPhone}
isError={!isValid}
helperText={!isValid ? 'Please enter a valid phone number' : 'We\'ll only use this to text you.'}
/>const inputRef = useRef<HTMLInputElement>(null);
<PhoneInput ref={inputRef} value={phone} onChange={setPhone} />;
// inputRef.current?.focus()The component accepts every prop of react-phone-number-input (e.g. countries, labels, flags, international, countryCallingCodeEditable, addInternationalOption, and native input attributes like id, name, required, autoComplete) plus the following:
| Prop | Type | Default | Description |
|---|---|---|---|
value |
string | undefined |
— | E.164 phone value (required) |
onChange |
(value: string | undefined) => void |
— | Change callback (required) |
defaultCountry |
Country |
"FR" |
Country selected by default |
isError |
boolean |
false |
Render the error state |
size |
"sm" | "md" | "lg" |
"md" |
Styling baseline |
theme |
PhoneInputTheme |
— | Design-token overrides |
containerStyle |
CSSProperties |
— | Inline style merged onto the container (wins) |
inputStyle |
CSSProperties |
— | Inline style merged onto the input (wins) |
className |
string |
— | Class on the container |
numberInputClassName |
string |
— | Class on the number input |
helperText |
ReactNode |
— | Content rendered below the control |
wrapperStyle |
CSSProperties |
— | Style on the outer wrapper |
wrapperClassName |
string |
— | Class on the outer wrapper |
Types are exported for convenience:
import type {
CustomPhoneInputProps,
PhoneInputTheme,
Country,
Value,
} from '@jbenbrahim/phone-input';- Reworked into a flexible, themeable component (
sizepresets +themetokens) - Full passthrough of the
react-phone-number-inputAPI and native input attributes - Ref forwarding,
helperText, and aPhoneInputalias - Configurable
defaultCountry; fixed focus tracking and container/input sizing
- Fixed styling issues when used as an npm package
- Converted Tailwind classes to inline styles for better compatibility
- Initial release