splitforms.com
LEAD CAPTURE · CONTACT FORM TEMPLATE

Phone Number Input Form (with masked input)

A contact form with a phone number field that auto-formats as the user types — (555) 123-4567 emerges from raw digits. Pure pattern validation, optional JS mask for the live formatting.

1,000/mo free·no card·works on any host
form.htmlhtml34 lines
01<form action="https://splitforms.com/api/submit" method="POST">
02 <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
03 <input type="hidden" name="subject" value="New detailed contact submission">
04
05 <label for="name">Full name *</label>
06 <input id="name" type="text" name="name" placeholder="Jane Builder" required>
07 <label for="email">Work email *</label>
08 <input id="email" type="email" name="email" placeholder="jane@company.com" required>
09 <label for="phone">Phone *</label>
10 <input id="phone" type="tel" name="phone" placeholder="+1 415 555 0142" required>
11 <label for="company">Company</label>
12 <input id="company" type="text" name="company" placeholder="Acme Inc">
13 <label for="reason">What's this about? *</label>
14 <select id="reason" name="reason" required>
15 <option value="">Choose…</option>
16 <option>Sales / pricing</option>
17 <option>Partnerships</option>
18 <option>Press / media</option>
19 <option>Support</option>
20 <option>Careers</option>
21 <option>Other</option>
22 </select>
23 <label for="message">Message *</label>
24 <textarea id="message" name="message" placeholder="Give us context — links, dates, deal size, anything we should know." required></textarea>
25
26 <!-- honeypot — bots fill every field -->
27 <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off">
28
29 <button type="submit">Send</button>
30</form>
31
32<p style="margin-top:12px;font-size:11px;color:#888;text-align:right">
33 Powered by <a href="https://splitforms.com" style="color:#888;text-decoration:none" target="_blank" rel="noopener">splitforms</a>
34</p>
1,000
submissions / mo, free
6
fields, ready to ship
5
code outputs
60s
from copy to inbox
§ 01Why it mattersthe qualifying-fields argument

Phone fields suffer from format chaos — users type +1 555 123 4567, (555) 123 4567, 555.123.4567, 5551234567. Three options: accept anything and normalize server-side (best for international), use a pattern attribute for client-side validation (good for US-only), or apply a JavaScript mask that formats as the user types (highest conversion, more code). splitforms accepts any format and your form's UX is whichever approach fits your audience.

Mobile-friendly numeric keypad, live mask, format-tolerant.
✦ at a glance
  • Detailed contact form · 6 fields
  • HTML, JS, React, PHP, cURL outputs
  • One POST endpoint, no SDK
  • Honeypot + classifier, no CAPTCHA
§ 02Live previewinteractive · sandboxed · no key required

See exactly what your visitors see — and you’ll receive.

Left: the rendered form, fully interactive in a sandboxed iframe. Right: the email and dashboard view that lands the moment a visitor submits.

preview · masked-phone-input-formlocalhost:3000
✦ what you’ll see in your inbox

Every submission becomes an email plus a dashboard row. The fields below are the exact payload your form will send. Reply-to is wired to the visitor’s email so hitting reply goes back to them.

dashboard · new submission14ms · 200 OK
SUBJECT · New detailed contact submission
Full name
Maya Iyer
Work email
maya@studio71.co
Phone
+1 415 555 0142
Company
Studio 71
What's this about?
Sales / pricing
Message
Loved your last open house in Hayes — looking for similar with parking. Pre-approved through Wells Fargo.

Iframe is sandboxed — submit doesn’t actually fire. Get your access key to wire it up live.

§ 03Three steps3 steps · ~60 seconds

Generate, embed, receive.

Three actions stand between you and your first lead. None of them require a backend, a database, or a CAPTCHA library.

STEP 01GENERATE

Use type="tel" not type="text"

Mobile keyboards open the numeric keypad for tel inputs. Desktop accepts the same characters as text but signals intent.

Create your form
key=sk_live_••••••••
STEP 02EMBED

Add a pattern attribute for validation

`pattern="[0-9() +-]+"` allows the common phone characters. Or use a strict US format: `pattern="\(?\d{3}\)?[ -]?\d{3}[ -]?\d{4}"`.

snippethtml
<form action="https://splitforms.com/api/submit" method="POST">
  …
</form>
STEP 03RECEIVE

(Optional) Add the JS mask

On input, strip non-digits and reformat to `(XXX) XXX-XXXX`. The mask runs on every keystroke, giving live formatting feedback.

inbox · 1 newjust now
FROM contact@yoursite.com
New detailed contact submission
Maya Iyer maya@studio71.co
Loved your last open house in Hayes — looking for similar with parking. Pre-approved through Wells Fargo.
§ 04Copy & ship5 languages · same endpoint

Five outputs. One backend.

HTML by default. Click open the language you ship in — every variant POSTs to the same /api/submit endpoint.

01HTMLform.html34 lines
<form action="https://splitforms.com/api/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
  <input type="hidden" name="subject" value="New detailed contact submission">

  <label for="name">Full name *</label>
  <input id="name" type="text" name="name" placeholder="Jane Builder" required>
  <label for="email">Work email *</label>
  <input id="email" type="email" name="email" placeholder="jane@company.com" required>
  <label for="phone">Phone *</label>
  <input id="phone" type="tel" name="phone" placeholder="+1 415 555 0142" required>
  <label for="company">Company</label>
  <input id="company" type="text" name="company" placeholder="Acme Inc">
  <label for="reason">What's this about? *</label>
  <select id="reason" name="reason" required>
    <option value="">Choose…</option>
    <option>Sales / pricing</option>
    <option>Partnerships</option>
    <option>Press / media</option>
    <option>Support</option>
    <option>Careers</option>
    <option>Other</option>
  </select>
  <label for="message">Message *</label>
  <textarea id="message" name="message" placeholder="Give us context — links, dates, deal size, anything we should know." required></textarea>

  <!-- honeypot — bots fill every field -->
  <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off">

  <button type="submit">Send</button>
</form>

<p style="margin-top:12px;font-size:11px;color:#888;text-align:right">
  Powered by <a href="https://splitforms.com" style="color:#888;text-decoration:none" target="_blank" rel="noopener">splitforms</a>
</p>
02JavaScriptform.js50 lines
<form id="lf-form">
  <label for="name">Full name *</label>
  <input id="name" type="text" name="name" placeholder="Jane Builder" required>
  <label for="email">Work email *</label>
  <input id="email" type="email" name="email" placeholder="jane@company.com" required>
  <label for="phone">Phone *</label>
  <input id="phone" type="tel" name="phone" placeholder="+1 415 555 0142" required>
  <label for="company">Company</label>
  <input id="company" type="text" name="company" placeholder="Acme Inc">
  <label for="reason">What's this about? *</label>
  <select id="reason" name="reason" required>
    <option value="">Choose…</option>
    <option>Sales / pricing</option>
    <option>Partnerships</option>
    <option>Press / media</option>
    <option>Support</option>
    <option>Careers</option>
    <option>Other</option>
  </select>
  <label for="message">Message *</label>
  <textarea id="message" name="message" placeholder="Give us context — links, dates, deal size, anything we should know." required></textarea>
  <button type="submit">Send</button>
</form>

<p style="margin-top:12px;font-size:11px;color:#888;text-align:right">
  Powered by <a href="https://splitforms.com" style="color:#888;text-decoration:none" target="_blank" rel="noopener">splitforms</a>
</p>

<script>
  document.getElementById('lf-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const data = new FormData(e.target);
    data.set('access_key', 'YOUR_ACCESS_KEY');
    data.set('subject', 'New detailed contact submission');

    const res = await fetch('https://splitforms.com/api/submit', {
      method: 'POST',
      body: data,
      headers: { Accept: 'application/json' },
    });

    const json = await res.json();
    if (json.success) {
      e.target.reset();
      alert('Sent!');
    } else {
      alert('Error: ' + (json.message || 'Try again'));
    }
  });
</script>
03React / Next.jsForm.tsx65 lines
'use client';

import { useState, type FormEvent } from 'react';

export default function ContactDetailedForm() {
  const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle');

  async function onSubmit(e: FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setStatus('sending');

    const data = new FormData(e.currentTarget);
    data.set('access_key', 'YOUR_ACCESS_KEY');
    data.set('subject', 'New detailed contact submission');

    const res = await fetch('https://splitforms.com/api/submit', {
      method: 'POST',
      body: data,
      headers: { Accept: 'application/json' },
    });

    const json = await res.json();
    setStatus(json.success ? 'sent' : 'error');
    if (json.success) e.currentTarget.reset();
  }

  if (status === 'sent') return <p>Thanks — we&rsquo;ll be in touch.</p>;

  return (
    <>
    <form onSubmit={onSubmit}>
      <label htmlFor="name">Full name *</label>
      <input id="name" type="text" name="name" placeholder="Jane Builder" required />
      <label htmlFor="email">Work email *</label>
      <input id="email" type="email" name="email" placeholder="jane@company.com" required />
      <label htmlFor="phone">Phone *</label>
      <input id="phone" type="tel" name="phone" placeholder="+1 415 555 0142" required />
      <label htmlFor="company">Company</label>
      <input id="company" type="text" name="company" placeholder="Acme Inc" />
      <label htmlFor="reason">What's this about? *</label>
      <select id="reason" name="reason" required>
        <option value="">Choose…</option>
        <option>Sales / pricing</option>
        <option>Partnerships</option>
        <option>Press / media</option>
        <option>Support</option>
        <option>Careers</option>
        <option>Other</option>
      </select>
      <label htmlFor="message">Message *</label>
      <textarea id="message" name="message" placeholder="Give us context — links, dates, deal size, anything we should know." required />

      <button type="submit" disabled={status === 'sending'}>
        {status === 'sending' ? 'Sending…' : 'Send'}
      </button>

      {status === 'error' && <p>Something went wrong. Try again.</p>}
    </form>

      <p style={{ marginTop: 12, fontSize: 11, color: '#888', textAlign: 'right' }}>
        Powered by <a href="https://splitforms.com" target="_blank" rel="noopener" style={{ color: '#888', textDecoration: 'none' }}>splitforms</a>
      </p>
    </>
  );
}
04PHPsubmit.php28 lines
<?php
// Drop into a PHP page. Receives a form POST and proxies it to splitforms.com.
// Useful when you want to add server-side validation or rate limiting.

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $allowed = ['name', 'email', 'phone', 'company', 'reason', 'message'];
    $payload = ['access_key' => 'YOUR_ACCESS_KEY'];
    $payload['subject'] = 'New detailed contact submission';

    foreach ($allowed as $f) {
        if (isset($_POST[$f])) $payload[$f] = $_POST[$f];
    }

    $ch = curl_init('https://splitforms.com/api/submit');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept: application/json']);
    $response = curl_exec($ch);
    $status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    header('Content-Type: application/json');
    http_response_code($status);
    echo $response;
    exit;
}
?>
05cURLtest.sh10 lines
curl -X POST https://splitforms.com/api/submit \
  -H "Accept: application/json" \
  -d "access_key=YOUR_ACCESS_KEY" \
  -d "subject=New detailed contact submission" \
  -d "name=Jane Builder" \
  -d "email=jane@example.com" \
  -d "phone=+15555555555" \
  -d "company=Jane Builder" \
  -d "reason=Sales / pricing" \
  -d "message=Hello from cURL" 

Replace YOUR_ACCESS_KEY with the key from your dashboard. That’s the only edit.

§ 04bUse this template with…25 frameworks · same backend

One template. Every framework.

The same field set works on every framework splitforms supports. HTML, React, Next.js, Vue, Astro, Hugo, WordPress — same POST, same backend.

Detailed contact form for HTML/forms/html/contact-detailedDetailed contact form for Next.js/forms/nextjs/contact-detailedDetailed contact form for React/forms/react/contact-detailedDetailed contact form for Vue/forms/vue/contact-detailedDetailed contact form for Astro/forms/astro/contact-detailedDetailed contact form for Svelte/forms/svelte/contact-detailedDetailed contact form for Webflow/forms/webflow/contact-detailedDetailed contact form for Carrd/forms/carrd/contact-detailedDetailed contact form for WordPress/forms/wordpress/contact-detailedDetailed contact form for Tailwind CSS/forms/tailwind/contact-detailedDetailed contact form for AJAX (vanilla JS)/forms/ajax/contact-detailedDetailed contact form for Hugo/forms/hugo/contact-detailedDetailed contact form for Gatsby/forms/gatsby/contact-detailedDetailed contact form for Eleventy/forms/eleventy/contact-detailedDetailed contact form for SvelteKit/forms/sveltekit/contact-detailedDetailed contact form for Framer/forms/framer/contact-detailedDetailed contact form for Nuxt/forms/nuxt/contact-detailedDetailed contact form for Alpine.js/forms/alpinejs/contact-detailedDetailed contact form for Bootstrap/forms/bootstrap/contact-detailedDetailed contact form for Jekyll/forms/jekyll/contact-detailedDetailed contact form for Vite/forms/vite/contact-detailedDetailed contact form for JavaScript/forms/javascript/contact-detailedDetailed contact form for Cloudflare Pages/forms/cloudflare/contact-detailedDetailed contact form for Netlify/forms/netlify/contact-detailedDetailed contact form for Vercel/forms/vercel/contact-detailed
§ 06FAQ4 answered

Things people ask before they ship.

Direct answers, no marketing fluff. Missing one? Email hello@splitforms.com.

01Why use type="tel" if it doesn't do anything special on desktop?
Two reasons. (1) Mobile keyboards: tel triggers the numeric keypad, dramatically reducing typos. (2) Accessibility: screen readers announce the field type, helping users with assistive tech understand what's expected. The 'no desktop benefit' is a feature, not a bug — the behavior matches user intent.
02Should I validate international or US-only?
Depends on your audience. International contact forms should accept any format (use a permissive pattern + server-side normalization). US-only forms can use a strict pattern that matches (555) 123-4567. Either way, splitforms accepts whatever format the user submits — the validation is for UX, not data integrity.
03Does the JS mask work with paste?
Yes — listen for both input and paste events, and reformat on every change. The pattern only validates on submit, so the mask gives live feedback as the user types or pastes.
04What about the libphonenumber-js library?
Google's libphonenumber (and its JS port) is the gold standard for international phone validation. It's ~80KB minified, so only use it if international validation accuracy is critical. For most contact forms, a pattern + a tel input is enough.
✻ ✻ ✻

Ship your phone number input form (with masked input) in 60 seconds.

1,000 free submissions per month. No credit card. Copy the snippet, paste your access key, watch leads land in your inbox.

Get free access key →Browse all 67 templates →
v0.1 · founders pricing locked in · early access open