> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loqate.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Phone Validation Implementation Guide

> Add phone validation to your website with copy-paste code examples

export const GistEmbed = ({url, height = "500px"}) => {
  const [htmlContent, setHtmlContent] = useState('');
  const [copied, setCopied] = useState(false);
  const [rawCode, setRawCode] = useState('');
  const [isDark, setIsDark] = useState(true);
  useEffect(() => {
    const checkTheme = () => {
      const htmlElement = document.documentElement;
      const computedStyle = window.getComputedStyle(htmlElement);
      const colorScheme = computedStyle.getPropertyValue('color-scheme');
      const isDarkMode = htmlElement.classList.contains('dark') || htmlElement.getAttribute('data-theme') === 'dark' || colorScheme.includes('dark') || window.matchMedia('(prefers-color-scheme: dark)').matches;
      setIsDark(isDarkMode);
    };
    checkTheme();
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
    mediaQuery.addEventListener('change', checkTheme);
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class', 'data-theme', 'style']
    });
    return () => {
      mediaQuery.removeEventListener('change', checkTheme);
      observer.disconnect();
    };
  }, []);
  useEffect(() => {
    fetch(url).then(r => r.text()).then(code => {
      setRawCode(code);
      const bgColor = isDark ? '#0a0c0e' : '#ffffff';
      const textColor = isDark ? '#c9d1d9' : '#24292e';
      const scrollTrack = isDark ? '#161b22' : '#f6f8fa';
      const scrollThumb = isDark ? '#30363d' : '#d1d5da';
      const scrollThumbHover = isDark ? '#484f58' : '#959da5';
      const iframe = `
          <!DOCTYPE html>
          <html>
            <head>
              <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
              <style>
                body {
                  margin: 0;
                  padding: 0;
                  background: ${bgColor};
                  overflow: auto;
                }
                pre {
                  margin: 0;
                  background: transparent !important;
                }
                code {
                  font-family: 'JetBrains Mono', JetBrains Mono, monospace !important;
                  font-size: 14px !important;
                  line-height: 1.7 !important;
                  background: transparent !important;
                  padding: 0 !important;
                }
                .hljs {
                  background: transparent !important;
                  color: ${textColor} !important;
                }
                ::-webkit-scrollbar {
                  width: 12px;
                  height: 12px;
                }
                ::-webkit-scrollbar-track {
                  background: ${scrollTrack};
                  border-radius: 10px;
                }
                ::-webkit-scrollbar-thumb {
                  background: ${scrollThumb};
                  border-radius: 10px;
                  border: 2px solid ${scrollTrack};
                }
                ::-webkit-scrollbar-thumb:hover {
                  background: ${scrollThumbHover};
                }
                * {
                  scrollbar-width: thin;
                  scrollbar-color: ${scrollThumb} ${scrollTrack};
                }
              </style>
            </head>
            <body>
              <pre><code class="language-html">${escapeHtml(code)}</code></pre>
              <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
              <script>hljs.highlightAll();</script>
            </body>
          </html>
        `;
      setHtmlContent(iframe);
    }).catch(e => {
      setRawCode('Error loading code');
      setHtmlContent('<pre>Error loading code</pre>');
    });
  }, [url, isDark]);
  const escapeHtml = text => {
    return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
  };
  const copyToClipboard = () => {
    navigator.clipboard.writeText(rawCode);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };
  const containerBg = isDark ? '#0a0c0e' : '#ffffff';
  const containerBorder = isDark ? '#30363d' : '#e1e4e8';
  return <div style={{
    position: 'relative',
    marginBottom: '1.5rem',
    marginTop: '1.5rem',
    background: containerBg,
    borderRadius: '12px',
    border: `1px solid ${containerBorder}`,
    padding: '16px 20px'
  }}>
      <button onClick={copyToClipboard} style={{
    position: 'absolute',
    top: '12px',
    right: '32px',
    padding: '8px',
    background: 'rgba(255, 255, 255, 0.08)',
    backdropFilter: 'blur(4px)',
    border: '1px solid rgba(255, 255, 255, 0.1)',
    borderRadius: '6px',
    cursor: 'pointer',
    fontSize: '13px',
    fontWeight: '500',
    color: 'rgba(255, 255, 255, 0.8)',
    zIndex: 10,
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    transition: 'all 0.15s',
    fontFamily: 'system-ui, -apple-system, sans-serif',
    width: '32px',
    height: '32px'
  }} onMouseEnter={e => {
    e.currentTarget.style.background = 'rgba(255, 255, 255, 0.12)';
  }} onMouseLeave={e => {
    e.currentTarget.style.background = 'rgba(255, 255, 255, 0.08)';
  }}>
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          {copied ? <polyline points="20 6 9 17 4 12" /> : <>
              <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
              <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
            </>}
        </svg>
      </button>
      <iframe srcDoc={htmlContent} style={{
    width: '100%',
    height: height,
    border: 'none',
    overflow: 'auto',
    background: 'transparent'
  }} title="Code Example" />
    </div>;
};

**Reduce errors and improve data quality by validating phone numbers at checkout, in contact forms, or during registration.**

## Who Should Use This Guide

**For:** Business owners and developers integrating phone validation without complex setup.

**You'll need:** A Loqate API key and basic HTML knowledge.

**Not covered:** Tag-based integrations ([see Tag Setup](/our-services/tag-setup-guide)) or pre-built platform integrations ([see Integrations](/integrations/all-integrations)).

## What You'll Build

A phone validation form that:

* Validates numbers in real-time
* Displays success or error states
* Prevents form submission with invalid numbers
* Works with international phone numbers

## Quick Start

To set up Phone Validation, you'll need an API key. Find out how to get yours [here](/loqate-basics/create-an-api-key/).

### Choose Your Approach

<Tabs>
  <Tab title="Standalone Form">
    **Use case:** Testing the API or adding to a static page

    A self-contained HTML file you can copy and use immediately.

    Jump to the [standalone HTML example](#standalone-html-example)
  </Tab>

  <Tab title="Integrate with Existing Form">
    **Use case:** Add validation to your current checkout or contact forms

    Step-by-step guide to add validation code to forms you already have.

    Jump to the [integration example](#integration-example)
  </Tab>
</Tabs>

**Using Shopify or WordPress?** Check [pre-built integrations](/integrations/all-integrations).

### Add Your Key

Replace `YOUR_API_KEY` in the code with your actual Loqate API key.

### Test

1. Open your page
2. Enter `+441244657333`
3. Validation result appears

<Note>
  Examples default to UK (GB) validation. See [Country Customization](#country-customization) to validate other countries.
</Note>

***

## Sample Project: Checkout Integration

This walkthrough shows how to add phone validation to a checkout form. Follow along to see a basic form transform into a validation-enabled checkout.

<Note>
  This is a detailed walkthrough. If you just want working code, skip to [Integration Example](#integration-example) or [Standalone HTML Example](#standalone-html-example).
</Note>

### Before: Basic Checkout

A checkout form without validation:

<GistEmbed url="https://raw.githubusercontent.com/Loqate/javascript-docs-sample/main/phone-validation/start/basic-checkout-form.html" height="450px" />

**This form works, but doesn't validate phone numbers.** Let's add validation.

***

## How Phone Validation Works

The complete implementation includes four key components:

### 1. Validation Function

A JavaScript function that sends phone numbers to the Loqate API and returns whether they're valid. The function handles API requests, error checking, and response parsing.

### 2. Visual Feedback

CSS classes and a message element that show users three states:

* **Checking:** Yellow background while validating
* **Valid:** Green background for valid numbers
* **Invalid:** Red background for invalid numbers

### 3. Real-Time Validation

An event listener on the phone input field that triggers validation when users finish typing (on blur). This provides immediate feedback without requiring form submission.

### 4. Form Submission Control

Logic that prevents form submission if the phone number is invalid, ensuring only validated data is processed.

***

## Standalone HTML Example

A minimal phone validation form for quick testing:

<GistEmbed url="https://raw.githubusercontent.com/Loqate/javascript-docs-sample/main/phone-validation/finish/simple-phone-validation.html" height="450px" />

**Perfect for:**

* Quick API testing
* Learning how phone validation works
* Simple integrations without checkout flows

**To use:**

1. Copy the code
2. Replace `YOUR_API_KEY`
3. Save and test with different phone numbers

<Note>
  This example defaults to validating UK (GB) numbers. See [Country Customization](#country-customization) to change countries.
</Note>

## Code on GitHub

If you'd like to browse the code snippets in this implementation guide or clone them, visit the repository on [GitHub](https://github.com/loqate/javascript-docs-sample/tree/main/phone-validation).

## Integration Example

Here's the checkout form with all validation components integrated:

<GistEmbed url="https://raw.githubusercontent.com/Loqate/javascript-docs-sample/main/phone-validation/finish/complete-checkout-with-validation.html" height="700px" />

**To use this code:**

1. Copy the code
2. Replace `YOUR_API_KEY` with your actual Loqate API key
3. Save as an HTML file
4. Open in a browser to test
5. Customize styling to match your brand

**Key features in this example:**

* Real-time validation on field exit
* Visual feedback for all validation states
* Form submission prevention for invalid numbers
* Success message display for valid submissions
* Clean, production-ready code structure

<Note>
  This example defaults to validating UK (GB) numbers. Change the `Country` parameter to validate other countries.
</Note>

***

## Integration Tips

### Adding to Your Existing Forms

To add phone validation to your current forms:

1. **Add the validation function** from the integration example to your page's `<script>` section
   * Handles API requests and response parsing

2. **Add validation styles** from the integration example to your page's `<style>` section
   * Provides visual feedback (valid/invalid/checking states)

3. **Add the validation message element** next to your phone input field
   * `<span id="phoneValidation"></span>`

4. **Add the event listeners** to trigger validation and control form submission
   * Blur event for real-time validation
   * Submit event to prevent invalid submissions

Reference the [integration example](#integration-example) above for all the code you need.

### Country Customization

Change the `Country` parameter in the API request to validate numbers from different countries:

* `'US'` - United States
* `'CA'` - Canada
* `'AU'` - Australia
* `'GB'` - United Kingdom (default in examples)

Or remove the `Country` parameter entirely to validate international format numbers that include the country prefix (e.g., `+441234567890` includes `+44` for UK).

### Validation Timing Options

**Current examples use:** Validation on field exit (blur event) - validates when users click or tab away from the phone field.

**Alternative approaches:**

**On button click:**
Add a separate "Validate" button that triggers validation manually.

**While typing (debounced):**
Validate as users type, with a short delay to avoid excessive API calls. Add a 1-second timeout that resets with each keystroke.

**On form submission only:**
Validate only when the user submits the form, showing errors before processing.

### Styling Customization

The examples use minimal styling. Customize the CSS classes (`.phone-valid`, `.phone-invalid`, `.phone-checking`) to match your brand:

* Change colors to match your design system
* Adjust padding and border-radius
* Add icons or animations
* Modify font sizes and weights

***

## Troubleshooting

### Invalid API Key

Verify you've replaced `YOUR_API_KEY` with your actual key from your Loqate account.

### Numbers Not Validating

**Check:**

* Country parameter matches the number format (`Country: 'GB'` for UK numbers)
* You have available credits in your account
* API key hasn't been restricted by domain, IP, or rate limits (check account settings)
* The phone number includes the country prefix if not using the Country parameter (e.g., `+44` for UK)

### Form Not Appearing

Ensure you copied the standalone HTML file including all `<style>` and `<script>` sections. The examples are self-contained and include all required code.

### CORS Errors

The examples must be hosted on a web server, not opened as local `file://` URLs. Upload to your website or use a development server like Python's `http.server` or Node's `http-server`.

***

## Data Privacy

* Numbers validated through Loqate's API
* Stored in infrastructure logs for 30 days for operational purposes
* Results returned immediately
* See [Privacy Policy](https://www.loqate.com/en-gb/legal/privacy-policy/) for complete details

## Support

* [API Reference](/api-reference/phone-validation/individual-validate)
* [Contact](https://www.loqate.com/en-gb/contact/)

## Related

* [Best Practices](/our-services/phone-validation/best-practices)
* [API Security](/loqate-basics/api-security)
* [Pricing](https://www.loqate.com/en-gb/pricing/)
