@foodhubglobal/phone
v0.1.12
Published
Phone number parsing, formatting and validation utilities built on libphonenumber-js, with TypeScript types and dual CommonJS/ES module builds for React Native, React and Node.js.
Readme
@foodhubglobal/phone
Phone number parsing, formatting and validation utilities built on libphonenumber-js, with TypeScript types and dual CommonJS/ES module builds.
Runs anywhere JavaScript does — React Native, React, Node.js and the browser. The package has no React or React Native dependency.
Installation
npm install @foodhubglobal/phone
# or
yarn add @foodhubglobal/phoneUsage in Different Environments
This library is compatible with multiple JavaScript/TypeScript environments. The phone module is a pure utility library that works in any JavaScript runtime.
1. React Native App
// App.tsx or any component file
import {
formatPhoneNumber,
validatePhoneNumber,
formatAsYouType
} from '@foodhubglobal/phone';
// Usage in React Native component
import React, { useState } from 'react';
import { TextInput, View, Text } from 'react-native';
function PhoneInput() {
const [phone, setPhone] = useState('');
return (
<View>
<TextInput
value={phone}
onChangeText={(text) => setPhone(formatAsYouType(text, 'US'))}
placeholder="Enter phone number"
/>
{validatePhoneNumber(phone, 'US') && (
<Text>Formatted: {formatPhoneNumber(phone, 'US')}</Text>
)}
</View>
);
}2. React.js (Web Application)
// Component.tsx or any React component
import {
formatPhoneNumber,
validatePhoneNumber,
formatAsYouType
} from '@foodhubglobal/phone';
// Usage in React component
import React, { useState } from 'react';
function PhoneInput() {
const [phone, setPhone] = useState('');
return (
<div>
<input
type="tel"
value={phone}
onChange={(e) => setPhone(formatAsYouType(e.target.value, 'US'))}
placeholder="Enter phone number"
/>
{validatePhoneNumber(phone, 'US') && (
<p>Formatted: {formatPhoneNumber(phone, 'US')}</p>
)}
</div>
);
}3. Node.js (API/Server Layer)
CommonJS (require)
// server.js or api.js
const {
formatPhoneNumber,
validatePhoneNumber,
parsePhone
} = require('@foodhubglobal/phone');
// Usage in Express.js API route
const express = require('express');
const app = express();
app.post('/validate-phone', (req, res) => {
const { phone, country } = req.body;
if (validatePhoneNumber(phone, country)) {
const formatted = formatPhoneNumber(phone, country);
res.json({ valid: true, formatted });
} else {
res.status(400).json({ valid: false, error: 'Invalid phone number' });
}
});
// Usage with detailed parsing
app.get('/parse-phone/:phone', (req, res) => {
const result = parsePhone(req.params.phone, req.query.country);
res.json(result);
});ES Modules (import) - Node.js 12+ or with "type": "module"
// server.mjs or package.json has "type": "module"
import {
formatPhoneNumber,
validatePhoneNumber,
parsePhone
} from '@foodhubglobal/phone';
// Usage in Express.js API route (ES Modules)
import express from 'express';
const app = express();
app.post('/validate-phone', (req, res) => {
const { phone, country } = req.body;
if (validatePhoneNumber(phone, country)) {
const formatted = formatPhoneNumber(phone, country);
res.json({ valid: true, formatted });
} else {
res.status(400).json({ valid: false, error: 'Invalid phone number' });
}
});TypeScript in Node.js
// api.ts or server.ts
import {
formatPhoneNumber,
validatePhoneNumber,
parsePhone,
type CountryCode
} from '@foodhubglobal/phone';
// Usage in Express.js API route (TypeScript)
import express, { Request, Response } from 'express';
const app = express();
app.post('/validate-phone', (req: Request, res: Response) => {
const { phone, country }: { phone: string; country?: CountryCode } = req.body;
if (validatePhoneNumber(phone, country)) {
const formatted = formatPhoneNumber(phone, country);
res.json({ valid: true, formatted });
} else {
res.status(400).json({ valid: false, error: 'Invalid phone number' });
}
});4. Pure JavaScript Project (Browser/Vanilla JS)
HTML with Script Tag (Browser)
<!-- Note: You'll need to bundle this first or use a CDN/bundler -->
<!DOCTYPE html>
<html>
<head>
<title>Phone Formatter</title>
</head>
<body>
<input type="tel" id="phoneInput" placeholder="Enter phone number">
<button onclick="formatPhone()">Format</button>
<p id="result"></p>
<script type="module">
// If using ES modules in browser
import { formatPhoneNumber, validatePhoneNumber } from './node_modules/@foodhubglobal/phone/lib/module/index.js';
window.formatPhone = function() {
const input = document.getElementById('phoneInput');
const result = document.getElementById('result');
const phone = input.value;
if (validatePhoneNumber(phone, 'US')) {
result.textContent = `Formatted: ${formatPhoneNumber(phone, 'US')}`;
} else {
result.textContent = 'Invalid phone number';
}
};
</script>
</body>
</html>Bundled JavaScript (Webpack/Rollup/Vite)
// app.js - Works with any bundler (Webpack, Rollup, Vite, etc.)
import {
formatPhoneNumber,
validatePhoneNumber
} from '@foodhubglobal/phone';
// Usage
function formatPhone(phoneNumber) {
if (validatePhoneNumber(phoneNumber, 'US')) {
return formatPhoneNumber(phoneNumber, 'US');
}
return 'Invalid phone number';
}
// Export for use in other files
export { formatPhone };Node.js Script (CLI/Tooling)
// script.js - Run with: node script.js
const { formatPhoneNumber, validatePhoneNumber } = require('@foodhubglobal/phone');
// CLI usage
const phoneNumber = process.argv[2];
const country = process.argv[3] || 'US';
if (validatePhoneNumber(phoneNumber, country)) {
console.log(`Valid! Formatted: ${formatPhoneNumber(phoneNumber, country)}`);
} else {
console.log('Invalid phone number');
process.exit(1);
}Modules
Phone Module
A comprehensive phone number formatting and validation module built on top of libphonenumber-js. Provides utilities for formatting, validating, and parsing phone numbers from around the world.
Installation
The phone module is included in the main package. No additional installation required.
Usage
import {
formatPhoneNumber,
formatInternational,
validatePhoneNumber,
formatAsYouType
} from '@foodhubglobal/phone';Examples
Format Phone Numbers
import { formatPhoneNumber, formatInternational, formatNational } from '@foodhubglobal/phone';
// E.164 format (default)
// With country code parameter (recommended for national numbers)
formatPhoneNumber('2125551234', 'US'); // => '+12125551234'
// Without country code (works when number includes country prefix)
formatPhoneNumber('+12125551234'); // => '+12125551234'
formatPhoneNumber('+442071234567'); // => '+442071234567'
// Fallback: Without country code for national number (returns original phoneNumber)
formatPhoneNumber('2125551234'); // => '2125551234' (returns original if parsing fails)
// With fallback parameter
formatPhoneNumber('invalid', undefined, 'N/A'); // => 'N/A' (returns fallback)
formatPhoneNumber('invalid'); // => 'invalid' (returns original phoneNumber)
// International format
formatInternational('2125551234', 'US'); // => '+1 212 555 1234'
formatInternational('02071234567', 'GB'); // => '+44 20 7123 4567'
formatInternational('+12125551234'); // => '+1 212 555 1234'
formatInternational('invalid', undefined, 'N/A'); // => 'N/A' (returns fallback)
// National format
formatNational('2125551234', 'US'); // => '(212) 555-1234'
formatNational('02071234567', 'GB'); // => '020 7123 4567'
formatNational('+12125551234'); // => '(212) 555-1234'
formatNational('invalid', undefined, 'N/A'); // => 'N/A' (returns fallback)Important: For national numbers (without country code prefix like +1), always provide the countryCode parameter for accurate results.
Error Handling: All format functions now support an optional fallback parameter. If parsing fails:
- With
fallbackprovided: returns thefallbackvalue - Without
fallback: returns the originalphoneNumber(for format functions) orundefined(for extract/get functions)
Validate Phone Numbers
import { validatePhoneNumber, isPossibleNumber } from '@foodhubglobal/phone';
validatePhoneNumber('+12125551234'); // => true
validatePhoneNumber('2125551234', 'US'); // => true
validatePhoneNumber('123', 'US'); // => false
isPossibleNumber('2125551234', 'US'); // => true
isPossibleNumber('123', 'US'); // => falseAs-You-Type Formatting
import { formatAsYouType } from '@foodhubglobal/phone';
// With country code parameter (recommended)
formatAsYouType('212', 'US'); // => '(212)'
formatAsYouType('212555', 'US'); // => '(212) 555'
formatAsYouType('2125551234', 'US'); // => '(212) 555-1234'
// Without country code (works when input includes country prefix)
formatAsYouType('+1212'); // => '+1 212'
formatAsYouType('+442071234567'); // => '+44 20 7123 4567'
// Use in TextInput for real-time formatting
<TextInput
value={phoneNumber}
onChangeText={(text) => setPhoneNumber(formatAsYouType(text, 'US'))}
/>Parse Phone Numbers
import { parsePhone } from '@foodhubglobal/phone';
// With country code parameter (recommended for national numbers)
const result1 = parsePhone('2125551234', 'US');
// => {
// isValid: true,
// isPossible: true,
// country: 'US',
// countryCallingCode: '1',
// nationalNumber: '2125551234',
// internationalFormat: '+1 212 555 1234',
// nationalFormat: '(212) 555-1234',
// e164Format: '+12125551234'
// }
// Without country code (works when number includes country prefix)
const result2 = parsePhone('+12125551234');
// => { ... same structure as above ... }
// Fallback: Without country code for national number (returns error object)
const result3 = parsePhone('2125551234');
// => {
// isValid: false,
// isPossible: false
// }Get Country Information
import {
getCountryFromPhoneNumber,
getCallingCode,
getAvailableCountries
} from '@foodhubglobal/phone';
getCountryFromPhoneNumber('+12125551234'); // => 'US'
getCountryFromPhoneNumber('+442071234567'); // => 'GB'
getCountryFromPhoneNumber('invalid', 'US'); // => 'US' (returns fallback)
getCallingCode('US'); // => '1'
getCallingCode('GB'); // => '44'
getCallingCode('IN'); // => '91'
getCallingCode('INVALID', 'N/A'); // => 'N/A' (returns fallback)
getAvailableCountries(); // => ['US', 'GB', 'CA', 'AU', 'IN', ...]Extract Phone Numbers
import { extractPhoneNumber } from '@foodhubglobal/phone';
extractPhoneNumber('Call me at +12125551234'); // => '+12125551234'
extractPhoneNumber('2125551234', 'US'); // => '+12125551234'
extractPhoneNumber('No phone here', undefined, 'N/A'); // => 'N/A' (returns fallback)
extractPhoneNumber('No phone here'); // => undefined (no fallback provided)Number Type Detection
import {
getNumberType,
isMobileNumber,
isFixedLineNumber
} from '@foodhubglobal/phone';
getNumberType('+12125551234'); // => 'MOBILE' | 'FIXED_LINE' | 'VOIP' | etc.
isMobileNumber('+12125551234'); // => true | false
isFixedLineNumber('+12125551234'); // => true | falseAdditional Utilities
import {
normalizePhoneNumber,
formatIncomplete,
getNationalNumber,
getCallingCodeFromNumber,
isSameNumber,
isValidForCountry,
formatRFC3966,
format
} from '@foodhubglobal/phone';
// Normalize phone number
normalizePhoneNumber('(212) 555-1234', 'US'); // => '+12125551234'
normalizePhoneNumber('invalid', undefined, 'N/A'); // => 'N/A' (returns fallback)
// Format incomplete numbers
formatIncomplete('212', 'US'); // => '(212)'
// Get national number
getNationalNumber('+12125551234'); // => '2125551234'
getNationalNumber('invalid', undefined, 'N/A'); // => 'N/A' (returns fallback)
getNationalNumber('invalid'); // => undefined (no fallback provided)
// Get calling code from number
getCallingCodeFromNumber('+12125551234'); // => '1'
getCallingCodeFromNumber('invalid', 'N/A'); // => 'N/A' (returns fallback)
getCallingCodeFromNumber('invalid'); // => undefined (no fallback provided)
// Compare two numbers
isSameNumber('+12125551234', '(212) 555-1234', 'US'); // => true
// Validate for specific country
isValidForCountry('2125551234', 'US'); // => true
// RFC3966 format
formatRFC3966('2125551234', 'US'); // => 'tel:+1-212-555-1234'
formatRFC3966('invalid', undefined, 'N/A'); // => 'N/A' (returns fallback)
// Custom format
format('2125551234', 'E.164', 'US'); // => '+12125551234'
format('2125551234', 'INTERNATIONAL', 'US'); // => '+1 212 555 1234'
format('invalid', 'E.164', undefined, 'N/A'); // => 'N/A' (returns fallback)Supported Countries
The phone module supports all countries supported by libphonenumber-js. Use getAvailableCountries() to get the full list of supported country codes.
Best Practices
When to provide countryCode parameter:
- ✅ Always provide when working with national numbers (e.g.,
'2125551234') - ✅ Optional when the number includes a country code prefix (e.g.,
'+12125551234') - ✅ Recommended for all formatting and validation functions to ensure accuracy
Example:
// ✅ Good: National number with country code
formatPhoneNumber('2125551234', 'US'); // => '+12125551234'
// ✅ Good: International format with prefix
formatPhoneNumber('+12125551234'); // => '+12125551234'
// ⚠️ May fail: National number without country code
formatPhoneNumber('2125551234'); // => undefined (ambiguous)Troubleshooting: Validation works in Staging but not Production
If phone validation behaves differently in production than in staging, check the following:
Pass
countryCodein both environments
National numbers (without a+prefix) require a country code. If prod uses a different config or API and omitscountryCode, validation will fail. Prefer always passing it:validatePhoneNumber(phone, countryCode).Country code format
Use ISO 3166-1 alpha-2 (e.g.'US','GB'). The library normalizes lowercase (e.g.'us'→'US'). Ensure prod is not sending a different format (e.g. numeric or 3-letter).Input normalization
The library trims whitespace and normalizes common Unicode digits to ASCII. If prod receives numbers from another system (e.g. with leading/trailing spaces or non-ASCII digits), they are normalized before validation. If you still see differences, log the exactphoneNumberandcountryCodein prod and compare to staging.Dependency version
Ensure staging and prod resolve the same version of@foodhubglobal/phone(and thuslibphonenumber-js). Lockfiles and install scripts should be identical so production does not get an older build.
TypeScript Support
The module is fully typed with TypeScript. All functions include comprehensive type definitions and JSDoc comments for better IDE support, including detailed parameter descriptions and fallback behavior documentation.
import type { CountryCode, NumberFormat, NumberType } from '@foodhubglobal/phone';Quick Reference: Import Methods
Depending on your environment, use the appropriate import method:
| Environment | CommonJS (require) | ES Modules (import) |
|------------|-------------------|---------------------|
| Node.js | ✅ require('@foodhubglobal/phone') | ✅ import from '@foodhubglobal/phone' |
| React.js | ✅ require('@foodhubglobal/phone') (ESM preferred) | ✅ import from '@foodhubglobal/phone' |
| React Native | ✅ require('@foodhubglobal/phone') (ESM preferred) | ✅ import from '@foodhubglobal/phone' |
| Pure JavaScript | ✅ require(...) | ✅ import from ... |
Note: These utilities are pure JavaScript functions. The package has no React or React Native dependency, so it can be used in any JavaScript environment. In React.js and React Native projects, bundlers such as Metro and common React build setups typically support both CommonJS (require) and ES modules (import); ES modules are recommended for new code, but CommonJS remains fully functional in these environments.
IDE Features:
- Auto-completion for all functions
- Inline documentation with JSDoc comments
- Type checking for parameters and return values
- Examples in tooltips
API Reference
Phone Module Functions
| Function | Description | Parameters | Returns |
|----------|-------------|------------|---------|
| formatPhoneNumber(phone, country?, fallback?) | Format in E.164 format | phone: string, country?: CountryCode, fallback?: string | string \| undefined |
| formatInternational(phone, country?, fallback?) | Format in international format | phone: string, country?: CountryCode, fallback?: string | string \| undefined |
| formatNational(phone, country?, fallback?) | Format in national format | phone: string, country?: CountryCode, fallback?: string | string \| undefined |
| validatePhoneNumber(phone, country?) | Validate phone number | phone: string, country?: CountryCode | boolean |
| isPossibleNumber(phone, country?) | Check if number is possible | phone: string, country?: CountryCode | boolean |
| formatAsYouType(phone, country?) | Format as user types | phone: string, country?: CountryCode | string |
| parsePhone(phone, country?) | Parse and get detailed info | phone: string, country?: CountryCode | object |
| getCountryFromPhoneNumber(phone, fallback?) | Extract country from number | phone: string (must include country prefix), fallback?: CountryCode | CountryCode \| undefined |
| getCallingCode(country, fallback?) | Get calling code for country | country: CountryCode, fallback?: string | string \| undefined |
| getAvailableCountries() | Get all supported countries | None | CountryCode[] |
| extractPhoneNumber(text, country?, fallback?) | Extract number from text | text: string, country?: CountryCode, fallback?: string | string \| undefined |
| getNumberType(phone, country?) | Get number type | phone: string, country?: CountryCode | NumberType \| undefined |
| isMobileNumber(phone, country?) | Check if mobile number | phone: string, country?: CountryCode | boolean |
| isFixedLineNumber(phone, country?) | Check if fixed-line number | phone: string, country?: CountryCode | boolean |
| normalizePhoneNumber(phone, country?, fallback?) | Normalize to E.164 | phone: string, country?: CountryCode, fallback?: string | string \| undefined |
| formatIncomplete(phone, country?) | Format incomplete number | phone: string, country?: CountryCode | string |
| getNationalNumber(phone, country?, fallback?) | Get national number | phone: string, country?: CountryCode, fallback?: string | string \| undefined |
| getCallingCodeFromNumber(phone, fallback?) | Get calling code from number | phone: string (must include country prefix), fallback?: string | string \| undefined |
| isSameNumber(phone1, phone2, country?) | Compare two numbers | phone1: string, phone2: string, country?: CountryCode | boolean |
| isValidForCountry(phone, country) | Validate for country | phone: string, country: CountryCode | boolean |
| formatRFC3966(phone, country?, fallback?) | Format in RFC3966 | phone: string, country?: CountryCode, fallback?: string | string \| undefined |
| format(phone, formatType, country?, fallback?) | Format with custom format | phone: string, formatType: NumberFormat, country?: CountryCode, fallback?: string | string \| undefined |
Note:
- The
country?parameter is optional but highly recommended for national numbers (without country code prefix). When omitted for national numbers, functions may returnundefinedor error objects due to ambiguity. For numbers with country code prefix (e.g.,+1...), the parameter is optional and the country is auto-detected. - The
fallback?parameter is optional and provides error handling:- Format functions (
formatPhoneNumber,formatInternational,formatNational,formatRFC3966,format,normalizePhoneNumber): If parsing fails andfallbackis provided, returnsfallback; otherwise returns the originalphoneNumber. - Extract/Get functions (
extractPhoneNumber,getNationalNumber,getCallingCodeFromNumber,getCountryFromPhoneNumber,getCallingCode): If parsing fails andfallbackis provided, returnsfallback; otherwise returnsundefined. - Parse function (
parsePhone): Returns an object withisValid: false, isPossible: falsewhen parsing fails (never returnsnull).
- Format functions (
Contributing
License
Proprietary. Copyright (c) 2025-2026 Foodhub. All rights reserved.
This package is licensed, not sold. You may install it and use it as a dependency within your own applications. You may not modify it, redistribute it on a standalone basis, or reverse engineer it. See LICENSE for the full terms.
Made with create-react-native-library
