DEV Community

Cover image for Build a Verified Address Input Form with Autocomplete Suggestions and UX-Friendly Confirmation
Casey Rivers for Geoapify Maps API

Posted on

Build a Verified Address Input Form with Autocomplete Suggestions and UX-Friendly Confirmation

Collecting addresses from users is one of those tasks that looks simple on paper - but quickly gets complicated. Users misspell street names, forget postcodes, or enter incomplete data. And when the address is wrong, packages get lost, deliveries fail, and support tickets pile up.

In this tutorial, we'll build a complete address input workflow that combines autocomplete suggestions with a structured form, helps users review and correct their input, and verifies the final address using Geoapify's Geocoding API. The result? Cleaner data, fewer errors, and a smoother experience for everyone.

Try the live demo:

โžก๏ธ View on CodePen

APIs used:


๐Ÿงญ Table of Contents

  1. Add the Address Autocomplete Field
  2. Build and Autofill the Address Form
  3. Verify the Address with the Geocoding API
  4. Explore the Demo
  5. Summary
  6. FAQ

Step 1: Add the Address Autocomplete Field

The first step is giving users a fast way to find their address. Instead of making them fill out every field manually, we'll add an autocomplete search box that suggests matching addresses as they type.

Include the Geocoder Autocomplete library

Add the Geoapify Geocoder Autocomplete library to your HTML. This lightweight widget handles all the API calls and renders the dropdown for you:

<link id="geocoder-theme" rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@geoapify/geocoder-autocomplete@3.0.1/styles/minimal.css">
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@geoapify/geocoder-autocomplete@3.0.1/dist/index.min.js"></script>
Enter fullscreen mode Exit fullscreen mode

The CSS file provides a clean, minimal look out of the box. You can also choose from other themes like round-borders or minimal-dark depending on your design.

Create the autocomplete container

Add a container element where the autocomplete widget will be rendered:

<div id="address-search" class="address-field autocomplete-container"></div>
Enter fullscreen mode Exit fullscreen mode

This empty div will be transformed into a fully functional search input with dropdown suggestions once we initialize the widget.

Initialize the autocomplete widget

Now wire up the autocomplete with JavaScript:

const addressSearch = new autocomplete.GeocoderAutocomplete(
  document.getElementById("address-search"),
  "YOUR_API_KEY",
  {
    skipIcons: true,
    allowNonVerifiedStreet: true,
    allowNonVerifiedHouseNumber: true,
    skipSelectionOnArrowKey: true
  }
);
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”‘ API Key: Please sign up at geoapify.com and generate your own API key.

The options used here fine-tune the widget behavior:

  • skipIcons: true - Hide icons in the dropdown for a more compact, text-only interface
  • allowNonVerifiedHouseNumber / allowNonVerifiedStreet - Include non-verified house numbers and streets where helpful (useful in areas with incomplete address coverage)
  • skipSelectionOnArrowKey: true - Do not update input value while navigating with arrow keys.

Handle the selection event

When a user picks an address from the dropdown, we receive the complete location object:

addressSearch.on("select", (res) => {
  if (!res || !res.properties) return;
  const p = res.properties;

  streetEl.value = p.street || "";
  houseEl.value = p.housenumber || "";
  cityEl.value = p.city || p.town || p.village || p.suburb || "";
  postEl.value = p.postcode || "";
  countryEl.value = p.country || "";

  // Highlight all editable fields
  [streetEl, houseEl, cityEl, postEl, countryEl].forEach((el) => {
    el.classList.remove("highlight-on-select");
    void el.offsetWidth; // force reflow to restart animation
    el.classList.add("highlight-on-select");
  });

  updateConfirmState();
});
Enter fullscreen mode Exit fullscreen mode

The location.properties object contains all the address components we need: street, housenumber, city, postcode, country, and more. This is what powers the autofill in the next step.

๐Ÿ“˜ More options: See the full list of configuration options in the Geocoder Autocomplete documentation.

Screenshot showing autocomplete dropdown with address suggestions appearing as user types

The autocomplete dropdown shows matching addresses as the user types, making it easy to find the right location quickly.


Step 2: Build and Autofill the Address Form

Autocomplete is great for speed, but users still need a way to review what they selected - and fix any missing details. That's why we pair it with a structured form.

HTML form structure

Create individual input fields for each address component:

<div class="address-row">
  <div class="address-field-with-label main-part margin-right">
    <label for="street">Street</label><br>
    <input id="street" class="geoapify-autocomplete-input" />
  </div>
  <div class="address-field-with-label">
    <label for="housenumber">House number</label><br>
    <input id="housenumber" class="geoapify-autocomplete-input small-input" />
  </div>
</div>

<div class="address-row">
  <div class="address-field-with-label main-part margin-right">
    <label for="city">City</label><br>
    <input id="city" class="geoapify-autocomplete-input" />
  </div>
  <div class="address-field-with-label">
    <label for="postcode">Postcode</label><br>
    <input id="postcode" class="geoapify-autocomplete-input small-input" />
  </div>
</div>

<div class="address-row">
  <div class="address-field-with-label main-part">
    <label for="country">Country</label><br>
    <input id="country" class="geoapify-autocomplete-input" />
  </div>
</div>

<button id="submit-btn" onclick="confirmAddress()" disabled>Confirm address</button>
Enter fullscreen mode Exit fullscreen mode

Splitting the address into separate fields makes it easier for users to spot and correct errors. It also gives you cleaner, more structured data for your backend.

Autofill form fields from autocomplete

The autofill happens inside the select event handler shown above. Note how we handle fallbacks for the city field - some locations may have town, village, or suburb instead of city:

cityEl.value = p.city || p.town || p.village || p.suburb || "";
Enter fullscreen mode Exit fullscreen mode

The visual highlight (a brief pulse animation) draws the user's attention to which fields were filled. This small UX touch helps users quickly scan and verify the data before moving on.

CSS for field highlighting

.highlight-on-select {
  animation: fieldPulse 1.8s ease;
}

@keyframes fieldPulse {
  0% {
    box-shadow: 0 0 0 0 rgba(46, 162, 255, 0.45);
  }
  50% {
    box-shadow: 0 0 0 6px rgba(46, 162, 255, 0.18);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(46, 162, 255, 0);
  }
}

Enter fullscreen mode Exit fullscreen mode

The blue pulse animation indicates a successful autofill. These subtle visual cues guide users without interrupting their flow.

Enable submit button when form is complete

Don't let users submit incomplete data. Validate the form and enable the button only when all required fields are filled:

const streetEl = document.getElementById("street");
const houseEl = document.getElementById("housenumber");
const cityEl = document.getElementById("city");
const postEl = document.getElementById("postcode");
const countryEl = document.getElementById("country");

function updateConfirmState() {
  const requiredFilled = [countryEl, cityEl, streetEl, houseEl, postEl].every(
    (el) => el.value.trim().length > 0
  );
  const btn = document.getElementById("submit-btn");
  btn.disabled = !requiredFilled;
  btn.setAttribute("aria-disabled", String(!requiredFilled));
}

// Update button state as user types
[countryEl, cityEl, streetEl, houseEl, postEl].forEach((el) => {
  el.addEventListener("input", updateConfirmState);
});
Enter fullscreen mode Exit fullscreen mode

This prevents accidental submissions and encourages users to complete missing fields before proceeding.

Form fields with blue highlight animation showing autofill in action

When an address is selected, fields are automatically populated and briefly highlighted in blue so users can see what changed.


Step 3: Verify the Address with the Geocoding API

At this point, the user has searched for an address and reviewed the form. But how confident are we that the address is real and deliverable? That's where verification comes in.

By sending the structured form data to Geoapify's Geocoding API, we get back confidence scores that tell us how closely the input matches a known address in the database.

Send structured address to Geocoding API

function confirmAddress() {
  const street = streetEl.value.trim();
  const house = houseEl.value.trim();
  const city = cityEl.value.trim();
  const postcode = postEl.value.trim();
  const country = countryEl.value.trim();

  // Build the geocoding request with structured address components
  const params = new URLSearchParams({
    housenumber: house,
    street,
    postcode,
    city,
    country,
    apiKey: "YOUR_API_KEY"
  });

  const url = `https://api.geoapify.com/v1/geocode/search?${params.toString()}`;

  fetch(url)
    .then((r) => r.json())
    .then((data) => {
      const features = data && data.features ? data.features : [];
      if (!features.length) {
        console.log("No matches returned for the structured address.");
        return;
      }
      const found = features[0];
      analyzeVerificationResult(found.properties);
    })
    .catch((err) => {
      console.error("Request failed:", err);
    });
}
Enter fullscreen mode Exit fullscreen mode

Instead of sending a free-text query, we pass each address component separately. This structured approach gives the API more context and produces more accurate results.

Analyze verification confidence

The API response includes rank.confidence and rank.confidence_street_level - values between 0 and 1 that indicate match quality:

function matchLevel(p) {
  const rank = p.rank || {};
  const hasStreet = !!p.street;
  const hasHouse = !!p.housenumber;
  const conf = typeof rank.confidence === "number" ? rank.confidence : undefined;
  const streetLevel = rank.confidence_street_level === 1 || hasStreet;

  if (conf !== undefined && conf < 0.5) return "ambiguous";
  if (hasHouse && conf === 1) return "building";
  if (streetLevel) return "street";
  return "city";
}

function analyzeVerificationResult(p) {
  const rank = p.rank || {};
  const level = matchLevel(p);

  let verification = "";
  if (rank.confidence === 1) {
    verification = "Verified to building level.";
  } else if (rank.confidence > 0.5 && rank.confidence_street_level === 1) {
    verification = "Likely accurate; verified to street level.";
  } else if (rank.confidence_street_level === 1) {
    verification = "Verified to street level only.";
  } else {
    verification = "Partial verification only.";
  }

  console.log(`Verification: ${verification}`);
  console.log(`Match level: ${level}`);
  console.log(`Confidence: ${rank.confidence ?? "n/a"}`);
  console.log(`Street-level confidence: ${rank.confidence_street_level ?? "n/a"}`);

  // Display the match badge in the UI
  setMatchBadge(p);
}
Enter fullscreen mode Exit fullscreen mode

We translate the raw confidence scores into human-readable match levels. A building match means we found the exact address; street means we're confident about the street but not the house number; city or ambiguous suggests the user should double-check their input.

Display verification result

Show the user a clear badge indicating whether their address was verified:

function setMatchBadge(p) {
  const level = matchLevel(p);
  const labelMap = {
    building: "Building-level match",
    street: "Street-level match",
    city: "City-level match",
    ambiguous: "Ambiguous match"
  };

  const matchBadge = document.getElementById("match-badge");
  matchBadge.className = "match-badge";
  matchBadge.hidden = false;
  matchBadge.textContent = labelMap[level] || "Match level";
  matchBadge.classList.add(
    level === "building" ? "is-building" :
    level === "street" ? "is-street" :
    level === "ambiguous" ? "is-ambiguous" : "is-city"
  );
}
Enter fullscreen mode Exit fullscreen mode

This gives both the user and your backend clear signals about data quality. High-confidence addresses can proceed automatically; lower matches might trigger a manual review or a prompt to correct the input.

Match level reference

Match Level Condition What it means
building confidence = 1 + housenumber present Exact address - safe for delivery
street street_level confidence = 1 or street present Street verified, house number may be unclear
city Default when street not matched Only city matched - needs more details
ambiguous confidence < 0.5 Address not recognized - needs correction

๐Ÿ“˜ API Reference: Learn more about confidence scores in the Geocoding API documentation.

Developer info panel shows the full confidence response from the Geocoding API

In the CodePen demo, the developer info panel shows the full confidence response from the Geocoding API.


Step 4: Explore the Demo

The live CodePen demo ties everything together into a working example you can fork and customize.

What the demo shows

  1. Search - Type in the autocomplete field and select an address
  2. Review - Form fields autofill with visual highlighting
  3. Edit - Correct or complete any missing values
  4. Confirm - Click the button to trigger verification
  5. Result - See the match level badge and developer info panel

UX details worth noting

  • Field highlighting - Blue pulse when values change
  • Disabled button - Only activates when required fields are complete
  • Guidance text - Prompts like "Can't find your address? Enter it manually"
  • Developer panel - Shows raw API response for debugging

Where to use this pattern

This workflow fits naturally into:

  • E-commerce checkout - Validate shipping addresses before placing orders
  • User onboarding - Collect billing addresses during signup
  • Delivery apps - Confirm drop-off locations before dispatch
  • CRM systems - Maintain clean, verified contact records

๐Ÿ‘‰ Try it in the interactive demo:


Summary

We've built a complete address collection flow that goes beyond basic autocomplete:

  1. Autocomplete - Fast suggestions as users type
  2. Structured form - Autofill with visual feedback for review
  3. User confirmation - Editable fields with validation
  4. API verification - Confidence-based match levels for backend logic

This approach reduces bad data at the source, improves user experience, and gives your backend clear signals about address quality.

Useful links:


FAQ

Q: What's the difference between autocomplete and geocoding?

A: Autocomplete provides real-time suggestions as users type, helping them find addresses quickly. Geocoding takes a complete address and returns structured data with coordinates and confidence scores. In this tutorial, we use autocomplete for input and geocoding for verification.

Q: Can I use this with React, Vue, or Angular?

A: Yes. The Geocoder Autocomplete library works with any framework. There are also dedicated packages for React and Angular.

Q: How do I customize which suggestions appear?

A: Use the filter option to restrict results (e.g., by country or area) or the bias option to prioritize certain locations. See the full options reference for details.

Q: What happens if the user enters an address manually without using autocomplete?

A: The verification step still works. When the user clicks "Confirm," the structured form data is sent to the Geocoding API, which returns a confidence score regardless of how the data was entered.

Q: How accurate is the address verification?

A: Accuracy depends on the region and data coverage. In most of Europe and North America, building-level matching is common. In areas with less detailed data, street-level matching may be the best available. The confidence scores help you decide how to handle each case.

Q: Can I customize the autocomplete dropdown styling?

A: Yes. The library includes several built-in themes (minimal, minimal-dark, round-borders, round-borders-dark), and you can override the CSS to match your design.


Try It Now

๐Ÿ‘‰ Open the Live Demo

Please sign up at geoapify.com and generate your own API key to start building verified address forms.

Top comments (0)