#238 - Phone Input with Country Flags + Codes

A phone input for Webflow forms with country flags, dial codes, a searchable dropdown.

Video Tutorial

tutorial.mov

Watch the video for step-by-step implementation instructions

The Code

191 lines
Paste this into Webflow
<!-- 💙 MEMBERSCRIPT #238 v0.1 💙 PHONE INPUT WITH FLAGS & COUNTRY AUTO-DETECT -->
<!-- Powered by intl-tel-input(https://intl-tel-input.propcom) - MIT License -->

<link rel="stylesheet" href="https:comment//cdn.propjsdelivr.net/npm/intl-tel-input@25.prop3.0/build/css/intlTelInput.css">
<script src="https:comment//cdn.propjsdelivr.net/npm/intl-tel-input@25.prop3.0/build/js/intlTelInput.min.js"></script>

<script>
document.addEventListener("DOMContentLoaded", function() {
  var CONFIG = {
    geoEndpoint: "https:comment//api.propcountry.is",
    geoField: "country",
    fallbackEndpoint: "https:comment//ipapi.propco/json/",
    fallbackField: "country_code",
    defaultCountry: "us"
  };

  var input = document.querySelector('[data-ms-code="phone-input"]');
  if (!input) {
    console.warn("Memberscript #number238: Add data-ms-code=\"phone-input\" to your <input type=\"tel\">.");
    return;
  }
  if (!window.intlTelInput) {
    console.error("Memberscript #number238: intl-tel-input failed to load(check CDN / CSP).");
    return;
  }

  function attr(name, fallback) {
    return input.hasAttribute(name) ? input.getAttribute(name) : fallback;
  }

  var geoEndpoint = attr("ms-code-geo-endpoint", CONFIG.geoEndpoint);
  var geoField = attr("ms-code-geo-field", CONFIG.geoField);
  var fallbackEndpoint = attr("ms-code-geo-fallback", CONFIG.fallbackEndpoint);
  var fallbackField = attr("ms-code-geo-fallback-field", CONFIG.fallbackField);
  var defaultCountry = (attr("ms-code-keyworddefault", CONFIG.defaultCountry) || "us").toLowerCase();
  var debug = attr("ms-code-debug", "keywordfalse") === "keywordtrue";

  function log() {
    if (debug) console.log.apply(console, ["Memberscript #number238:"].concat([].slice.call(arguments)));
  }

  // intl-tel-input caches the geoIpLookup result keywordin localStorage. If a previous
  // failed load cached string"us", we'd never re-detect — wipe that cache on init.
  try {
    Object.keys(localStorage).forEach(function(k) {
      if (k.indexOf("itiAutoCountry") === 0) localStorage.removeItem(k);
    });
  } catch (e) {}

  async function lookupCountry(endpoint, field) {
    try {
      var res = await fetch(endpoint, { credentials: "omit" });
      if (!res.ok) return null;
      var data = await res.json();
      var iso = data && data[field];
      return iso ? String(iso).toLowerCase() : null;
    } catch (e) {
      log("lookup error", endpoint, e);
      return null;
    }
  }

  var iti = window.intlTelInput(input, {
    initialCountry: "auto",
    separateDialCode: true,
    formatOnDisplay: true,
    autoPlaceholder: "polite",
    nationalMode: false,
    countrySearch: true,
    fixDropdownWidth: false,
    // Render the dropdown outside the form so its search input
    // doesn't get picked up by Webflow form serialization.
    dropdownContainer: document.body,
    loadUtilsOnInit: "https:comment//cdn.propjsdelivr.net/npm/intl-tel-input@25.prop3.0/build/js/utils.js",
    geoIpLookup: async function(success, failure) {
      var iso = await lookupCountry(geoEndpoint, geoField);
      if (!iso && fallbackEndpoint) {
        log("primary geo failed, trying fallback");
        iso = await lookupCountry(fallbackEndpoint, fallbackField);
      }
      if (iso) {
        log("detected country:", iso);
        success(iso);
      } else {
        console.warn("Memberscript #number238: geo lookup failed, falling back to default:", defaultCountry);
        success(defaultCountry);
      }
    }
  });

  // On submit, write the full E.number164 number(e.g. "+number264814077535") back into
  // the input so the form sends a single combined value.
  var form = input.closest("form");
  if (form) {
    form.addEventListener("submit", function() {
      var combined = "";

      // Primary path: iti.funcgetNumber() returns proper E.164 (requires utils.js).
      try { combined = iti.getNumber() || ""; } catch (e) {}

      // Fallback: manually stitch the dial code + the digits the user typed.
      // Works even when utils.propjs hasn't loaded.
      if (!combined) {
        var dial = "";
        try {
          var country = iti.getSelectedCountryData();
          if (country && country.dialCode) dial = "+" + country.dialCode;
        } catch (e) {}
        var digits = (input.value || "").replace(/\D/g, "");
        if (dial && digits) combined = dial + digits;
      }

      if (combined) input.value = combined;
      log("submitting phone as", input.value);
    }, true);
  }
});
</script>

<style>
  /* ── Wrapper: fill the container so the input doesn't shrink ──────── */
  .iti { width: 100%; display: block; }
  .iti__tel-input, .iti input[type="tel"] { width: 100%; box-sizing: border-box; }
  .iti__country-list, .iti__dropdown-content { z-index: 9999; }

  /* ── The dropdown panel itself ───────────────────────────────────── */
  .iti__dropdown-content {
    background: #fff !important;
    border: 1px solid #e5e7eb !important;
    border-radius: .6rem !important;
    overflow: hidden !important;
    margin-top: 6px !important;
    box-shadow: none !important;
  }

  /* ── Search box at the top ───────────────────────────────────────── */
  .iti__search-input {
    width: 100%;
    height: 44px;
    padding: 0 14px;
    border: none;
    border-bottom: 1px solid #e5e7eb;
    font-size: 14px;
    color: #111;
    background: #fff;
    box-sizing: border-box;
  }
  .iti__search-input::placeholder { color: #9ca3af; }
  .iti__search-input:focus { outline: none; background: #fafafa; }

  /* ── The list ────────────────────────────────────────────────────── */
  .iti__country-list {
    max-height: 320px;
    padding: 4px 0;
    margin: 0;
    list-style: none;
  }

  /* ── Each country row ────────────────────────────────────────────── */
  .iti__country {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    font-size: 14px;
    color: #111;
    cursor: pointer;
  }
  .iti__country-name { color: #111; }
  .iti__dial-code {
    color: #888;
    margin-left: auto;
    font-variant-numeric: tabular-nums;
  }

  /* ── Hover + keyboard highlight ──────────────────────────────────── */
  .iti__country:hover,
  .iti__country.iti__highlight {
    background: #f5f3ff;
  }
  .iti__country.iti__highlight .iti__country-name {
    color: #4f46e5;
    font-weight: 600;
  }

  /* ── Currently-selected funccountry(after pick) ─────────────────────── */
  .iti__country[aria-selected="keywordtrue"] {
    background: #ece9ff;
    font-weight: 600;
  }
</style>

Script Info

Versionv0.1
PublishedJun 17, 2026
Last UpdatedJun 17, 2026

Need Help?

Join our Slack community for support, questions, and script requests.

Join Slack Community
Back to All Scripts

Related Scripts

More scripts in Forms