واجهة برمجة الاكمال التلقائي للعناوين: كيف يرفع حقل واحد معدل تحويل الدفع بنسبة 35٪
Tutorials

واجهة برمجة الاكمال التلقائي للعناوين: كيف يرفع حقل واحد معدل تحويل الدفع بنسبة 35٪

اضف الاكمال التلقائي للعناوين الى صفحة الدفع في React باستخدام MapAtlas Geocoding API. قلل من التخلي عن سلة التسوق وسرع الادخال على الجوال وتحقق من العناوين وقت الادخال.

MapAtlas Team10 min read
#address autocomplete#geocoding api#checkout conversion#address validation#javascript autocomplete

The checkout address field is where mobile conversions go to die. A user navigates to your product page, adds an item to their cart, proceeds to checkout, and then they're asked to type out their full street address on a 6-inch touchscreen keyboard. If they mistype the postal code, get the house number format wrong, or simply give up, you've lost a sale you'd already won.

Address autocomplete fixes this. After implementation, address entry drops from 15–25 keystrokes to 3–4. The user starts typing a street name, sees a matching suggestion within half a second, taps it, and the entire address, street, house number, city, postal code, country, fills in automatically and correctly. Failed deliveries caused by typos drop. Checkout abandonment drops. And crucially, you have verified, geocoded coordinates attached to every order, which your logistics and routing systems can use directly.

Research across e-commerce implementations consistently shows 25–35% improvement in checkout completion rates after address autocomplete is added, with the effect significantly stronger on mobile, where manual text entry is slowest and most error-prone. Some implementations targeting mobile-first markets report the full 35%.

This tutorial builds a complete React address autocomplete component using the MapAtlas Geocoding API, including debouncing, keyboard navigation, EU address format handling, and form integration. The complete component is about 90 lines.

Why Address Errors Kill Conversions

Failed deliveries are expensive in every direction: the carrier charges a redelivery fee, your customer service team handles the complaint, and the customer's trust in your brand takes a hit. In B2C e-commerce, address entry errors account for approximately 5–8% of all shipping exceptions.

The underlying causes are predictable:

  • Mobile keyboard entry produces more typos than desktop. Autocorrect frequently corrupts street names and city names.
  • Postal code formats vary by country. A German customer entering a 5-digit code in a field expecting UK format (AN NAA) will trigger a validation error.
  • Street/house number ordering differs across EU countries. In Germany and the Netherlands, house number follows the street name. In France, it precedes it. Manual entry forms rarely guide users correctly.
  • Apartment and floor designations have no standardized format. Users enter them in whatever format feels natural, which often doesn't match what your shipping carrier expects.

Autocomplete sidesteps most of these problems by returning a pre-validated, structured address object. The user selects what they intend, and your form receives the correct format.

The MapAtlas Geocoding Autocomplete Endpoint

The endpoint for autocomplete suggestions is:

GET https://api.mapatlas.eu/geocoding/v1/autocomplete?text={query}&key={YOUR_API_KEY}

Optional parameters that matter for EU e-commerce:

ParameterTypeDescription
textstringThe partial address query
focus.point.lonnumberUser's longitude (prioritizes nearby results)
focus.point.latnumberUser's latitude (prioritizes nearby results)
boundary.countrystringISO 3166-1 alpha-3 country code (e.g., DEU, FRA, NLD)
layersstringFilter result types: address, street, locality
sizenumberNumber of results (default 10, max 20)

A typical response:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": { "type": "Point", "coordinates": [4.9041, 52.3676] },
      "properties": {
        "id": "address:node/1234567",
        "label": "Damrak 1, 1012 LG Amsterdam, Netherlands",
        "name": "Damrak 1",
        "street": "Damrak",
        "housenumber": "1",
        "postalcode": "1012 LG",
        "locality": "Amsterdam",
        "region": "North Holland",
        "country": "Netherlands",
        "country_code": "NL",
        "confidence": 0.98
      }
    }
  ]
}

Every result comes back as a GeoJSON feature with structured address components. Your form receives clean, validated data you can insert directly into each field, or store as a single object alongside the coordinates for routing and delivery planning.

Address autocomplete UX flow

[Image: Screenshot sequence showing the address autocomplete in action: empty field → user types "Dam" → dropdown appears with 4 suggestions → user taps "Damrak 1, Amsterdam" → all form fields (street, city, postal code) populate simultaneously.]

Building the React Autocomplete Hook

Start by extracting the API logic into a reusable hook. This keeps the component clean and makes the hook testable independently.

// hooks/useAddressAutocomplete.js
import { useState, useEffect, useRef } from 'react';

const API_BASE = 'https://api.mapatlas.eu/geocoding/v1/autocomplete';
const API_KEY  = process.env.NEXT_PUBLIC_MAPATLAS_KEY;
const DEBOUNCE_MS = 300;
const MIN_CHARS   = 3;

export function useAddressAutocomplete(countryCode = null) {
  const [query,       setQuery]       = useState('');
  const [suggestions, setSuggestions] = useState([]);
  const [loading,     setLoading]     = useState(false);
  const [error,       setError]       = useState(null);
  const debounceTimer = useRef(null);

  useEffect(() => {
    if (query.length < MIN_CHARS) {
      setSuggestions([]);
      return;
    }

    clearTimeout(debounceTimer.current);

    debounceTimer.current = setTimeout(async () => {
      setLoading(true);
      setError(null);

      try {
        const url = new URL(API_BASE);
        url.searchParams.set('text', query);
        url.searchParams.set('key', API_KEY);
        url.searchParams.set('size', '6');
        url.searchParams.set('layers', 'address');
        if (countryCode) {
          url.searchParams.set('boundary.country', countryCode);
        }

        const res  = await fetch(url.toString());
        if (!res.ok) throw new Error(`API error: ${res.status}`);

        const data = await res.json();
        setSuggestions(data.features ?? []);
      } catch (err) {
        setError(err.message);
        setSuggestions([]);
      } finally {
        setLoading(false);
      }
    }, DEBOUNCE_MS);

    return () => clearTimeout(debounceTimer.current);
  }, [query, countryCode]);

  return { query, setQuery, suggestions, loading, error };
}

The debounce timer fires only after the user pauses typing for 300ms. The MIN_CHARS guard prevents API calls on 1–2 character inputs where results would be too broad to be useful. Both measures are critical for keeping API usage (and costs) proportional to actual user intent.

The Autocomplete Component

// components/AddressAutocomplete.jsx
import { useState, useRef } from 'react';
import { useAddressAutocomplete } from '../hooks/useAddressAutocomplete';

export function AddressAutocomplete({ onSelect, countryCode, placeholder }) {
  const { query, setQuery, suggestions, loading } = useAddressAutocomplete(countryCode);
  const [open,        setOpen]    = useState(false);
  const [highlighted, setHighlighted] = useState(-1);
  const inputRef = useRef(null);

  function handleSelect(feature) {
    const p = feature.properties;
    setQuery(p.label);
    setOpen(false);
    setHighlighted(-1);
    onSelect({
      label:       p.label,
      street:      p.street      ?? '',
      housenumber: p.housenumber ?? '',
      postalcode:  p.postalcode  ?? '',
      locality:    p.locality    ?? '',
      region:      p.region      ?? '',
      country:     p.country     ?? '',
      country_code: p.country_code ?? '',
      coordinates: feature.geometry.coordinates, // [lng, lat]
    });
  }

  function handleKeyDown(e) {
    if (!open || suggestions.length === 0) return;
    if (e.key === 'ArrowDown')  setHighlighted(h => Math.min(h + 1, suggestions.length - 1));
    if (e.key === 'ArrowUp')    setHighlighted(h => Math.max(h - 1, 0));
    if (e.key === 'Enter' && highlighted >= 0) handleSelect(suggestions[highlighted]);
    if (e.key === 'Escape')     setOpen(false);
  }

  return (
    <div style={{ position: 'relative' }}>
      <input
        ref={inputRef}
        type="text"
        value={query}
        placeholder={placeholder ?? 'Start typing your address...'}
        onChange={e => { setQuery(e.target.value); setOpen(true); setHighlighted(-1); }}
        onKeyDown={handleKeyDown}
        onBlur={() => setTimeout(() => setOpen(false), 150)}
        style={{ width: '100%', padding: '10px 12px', fontSize: 16, borderRadius: 6, border: '1px solid #ccc' }}
        autoComplete="off"
        aria-autocomplete="list"
        aria-haspopup="listbox"
        aria-expanded={open && suggestions.length > 0}
      />

      {loading && (
        <span style={{ position: 'absolute', right: 12, top: '50%', transform: 'translateY(-50%)', fontSize: 12, color: '#888' }}>
          Searching…
        </span>
      )}

      {open && suggestions.length > 0 && (
        <ul
          role="listbox"
          style={{
            position: 'absolute', top: '100%', left: 0, right: 0, zIndex: 999,
            background: '#fff', border: '1px solid #ccc', borderTop: 'none',
            borderRadius: '0 0 6px 6px', listStyle: 'none', margin: 0, padding: 0,
            boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
          }}
        >
          {suggestions.map((feature, i) => (
            <li
              key={feature.properties.id}
              role="option"
              aria-selected={i === highlighted}
              onMouseDown={() => handleSelect(feature)}
              onMouseEnter={() => setHighlighted(i)}
              style={{
                padding: '10px 12px',
                cursor: 'pointer',
                fontSize: 14,
                background: i === highlighted ? '#f0f7e6' : '#fff',
                borderBottom: i < suggestions.length - 1 ? '1px solid #f0f0f0' : 'none',
              }}
            >
              {feature.properties.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

The component handles full keyboard navigation (arrow keys, enter, escape), ARIA attributes for screen reader compatibility, and a 150ms blur delay so mouse clicks on suggestions register before the list closes.

Integrating With a Checkout Form

// pages/checkout.jsx
import { useState } from 'react';
import { AddressAutocomplete } from '../components/AddressAutocomplete';

export default function CheckoutPage() {
  const [address, setAddress] = useState({
    street: '', housenumber: '', postalcode: '',
    locality: '', country: '', coordinates: null,
  });

  function handleAddressSelect(selected) {
    setAddress(selected);
    // Coordinates are available for routing/delivery estimation
    console.log('Delivery coordinates:', selected.coordinates);
  }

  return (
    <form>
      <h2>Delivery address</h2>

      <AddressAutocomplete
        onSelect={handleAddressSelect}
        countryCode="NLD"  // Restrict to Netherlands, remove for EU-wide
        placeholder="Start typing your street address..."
      />

      {/* Show structured fields after selection, allow manual edits */}
      {address.street && (
        <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 8, marginTop: 12 }}>
          <input value={address.street}      onChange={e => setAddress(a => ({ ...a, street: e.target.value }))}      placeholder="Street" />
          <input value={address.housenumber} onChange={e => setAddress(a => ({ ...a, housenumber: e.target.value }))} placeholder="No." style={{ width: 80 }} />
          <input value={address.postalcode}  onChange={e => setAddress(a => ({ ...a, postalcode: e.target.value }))}  placeholder="Postal code" />
          <input value={address.locality}    onChange={e => setAddress(a => ({ ...a, locality: e.target.value }))}    placeholder="City" />
        </div>
      )}

      <button type="submit" style={{ marginTop: 16 }}>
        Continue to payment
      </button>
    </form>
  );
}

Showing individual editable fields after autocomplete is important for accessibility and edge cases. A user's actual door address may include a flat number or access code that the geocoded result doesn't include. The autocomplete fills the validated base address; the user adds the rest.

Checkout form with address autocomplete active

[Image: Mobile device showing a checkout form. The address input field has a dropdown below it with 5 address suggestions. The first suggestion is highlighted. Below the input, the street, house number, postal code, and city fields are pre-filled from a previous selection.]

EU Address Format Considerations

Different EU countries have address conventions that affect both display and form field order:

Germany (DEU): Street first, house number after. Hauptstraße 42, 10115 Berlin. The housenumber property from the API correctly follows the street in German results.

France (FRA): House number before street. 42 rue de Rivoli, 75001 Paris. The label property returns addresses in the country-appropriate format.

Netherlands (NLD): Dutch postal codes are 4 digits + 2 uppercase letters with a space: 1012 LG. Validate this format if you're splitting the postal code for your shipping system.

Belgium (BEL): Bilingual regions may return addresses in French or Dutch depending on the municipality.

The MapAtlas Geocoding API handles all of these correctly in the label field (human-readable, country-appropriate) while also returning structured street, housenumber, and postalcode fields so you can build country-specific form layouts if needed.

For bulk validation of existing address databases, perhaps cleaning a legacy CRM before launching a delivery service, see How to Use the Geocoding API to Validate 10,000 Addresses in Bulk.

Performance Considerations

The implementation above makes approximately one API call per 3–4 characters typed on average (with the 300ms debounce absorbing fast typing). For an e-commerce site with meaningful checkout volume, set a server-side proxy in front of the Geocoding API so your API key never appears in client-side code:

// pages/api/autocomplete.js (Next.js API route)
export default async function handler(req, res) {
  const { text, countryCode } = req.query;
  const url = new URL('https://api.mapatlas.eu/geocoding/v1/autocomplete');
  url.searchParams.set('text',  text);
  url.searchParams.set('key',   process.env.MAPATLAS_KEY); // Server-side env var
  url.searchParams.set('size',  '6');
  url.searchParams.set('layers', 'address');
  if (countryCode) url.searchParams.set('boundary.country', countryCode);

  const response = await fetch(url.toString());
  const data     = await response.json();
  res.json(data);
}

Then update the hook to call /api/autocomplete instead of the MapAtlas API directly. This approach also lets you add request caching at the edge layer (Vercel Edge Functions, Cloudflare Workers) to reduce API calls for common queries.

See the MapAtlas Pricing page for current Geocoding API rates and free tier limits, for most e-commerce implementations, autocomplete usage fits comfortably in the free tier during development.

Summary

A single address autocomplete field can meaningfully move your checkout conversion rate. The implementation is straightforward: a debounced fetch to the MapAtlas Geocoding API, a small dropdown component with keyboard navigation, and form integration that fills structured fields from the selected result.

Key decisions:

  • Debounce at 300ms to avoid excessive API calls on fast typists.
  • Require 3 characters before triggering requests.
  • Restrict by country if you know your user base geography, it improves result relevance significantly.
  • Always allow manual editing of autocompleted fields for flat numbers, access codes, and corrections.
  • Proxy the API key server-side for production to avoid exposing credentials in client bundles.

For your first map integration alongside the address field, see How to Add Interactive Maps to Your Website to show the delivery location on a confirmation page.


Sign up for a free MapAtlas API key to start building. The Geocoding API is included in the free tier, no credit card required.

وجدت هذا مفيداً؟ شاركه.

Back to Blog