Het adresveld in de checkout is waar mobiele conversies verloren gaan. Een gebruiker navigeert naar uw productpagina, voegt een artikel toe aan het winkelwagentje, gaat naar de checkout en moet vervolgens een volledig adres intypen op een touchscreentoetsenbord van 15 centimeter. Als ze de postcode verkeerd intypen, het huisnummerformaat fout hebben, of gewoon afhaken, bent u een verkoop kwijt die u al had gewonnen.
Adresautocomplete lost dit op. Na implementatie daalt de adresinvoer van 15 tot 25 toetsaanslagen naar 3 tot 4. De gebruiker begint een straatnaam in te typen, ziet binnen een halve seconde een passende suggestie, tikt erop, en het volledige adres, straat, huisnummer, stad, postcode, land, wordt automatisch en correct ingevuld. Mislukte leveringen door typefouten nemen af. Checkoutafhakers nemen af. En u heeft geverifieerde, gegeocodeerde coördinaten bij elke bestelling, die uw logistieke en routesystemen direct kunnen gebruiken.
Onderzoek naar e-commerceimplementaties toont consistent 25 tot 35% verbetering in checkoutafronding na het toevoegen van adresautocomplete. Het effect is aanzienlijk sterker op mobiel, waar handmatige tekstinvoer het langzaamst en meest foutgevoelig is. Sommige implementaties gericht op mobiel-eerste markten halen de volledige 35%.
Deze tutorial bouwt een volledig React-adresautocompletecomponent met de MapAtlas Geocoding API, inclusief debouncing, toetsenbordnavigatie, EU-adresformaatverwerking en formulierintegatie. Het volledige component is circa 90 regels.
Waarom adresfouten conversies vernielen
Mislukte leveringen zijn in alle richtingen duur: de vervoerder rekent een hertarief, uw klantenserviceteam handelt de klacht af, en het vertrouwen van de klant in uw merk krijgt een deuk. In B2C-e-commerce zijn adresinvoerfouten verantwoordelijk voor ongeveer 5 tot 8% van alle verzendingsuitzonderingen.
De onderliggende oorzaken zijn voorspelbaar:
- Mobiele toetsenbordinvoer produceert meer typefouten dan desktop. Autocorrect beschadigt regelmatig straatnamen en plaatsnamen.
- Postcodeformaten verschillen per land. Een Duitse klant die een 5-cijferige code invoert in een veld dat het Britse formaat verwacht, triggert een validatiefout.
- Straat/huisnummerindeling verschilt per EU-land. In Duitsland en Nederland volgt het huisnummer de straatnaam. In Frankrijk staat het ervoor. Handmatige invoerformulieren begeleiden gebruikers zelden correct.
- Appartement- en verdiepingsaanduidingen hebben geen gestandaardiseerd formaat. Gebruikers voeren ze in welk formaat dan ook in, wat vaak niet overeenkomt met wat uw verzendpartner verwacht.
Autocomplete omzeilt de meeste van deze problemen door een vooraf gevalideerd, gestructureerd adresobject terug te geven. De gebruiker selecteert wat ze bedoelen, en uw formulier ontvangt het correcte formaat.
Het MapAtlas Geocoding Autocomplete-eindpunt
Het eindpunt voor autocompletesuggesties is:
GET https://api.mapatlas.eu/geocoding/v1/autocomplete?text={query}&key={YOUR_API_KEY}
Optionele parameters die relevant zijn voor EU-e-commerce:
| Parameter | Type | Omschrijving |
|---|---|---|
text | string | De gedeeltelijke adresquery |
focus.point.lon | number | Lengtegraad van de gebruiker (geeft prioriteit aan nabijgelegen resultaten) |
focus.point.lat | number | Breedtegraad van de gebruiker (geeft prioriteit aan nabijgelegen resultaten) |
boundary.country | string | ISO 3166-1 alpha-3 landcode (bijv. DEU, FRA, NLD) |
layers | string | Resultaattypen filteren: address, street, locality |
size | number | Aantal resultaten (standaard 10, max 20) |
Een typisch antwoord:
{
"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
}
}
]
}
Elk resultaat wordt geretourneerd als een GeoJSON-feature met gestructureerde adrescomponenten. Uw formulier ontvangt schone, gevalideerde gegevens die u direct in elk veld kunt invoegen, of als één object kunt opslaan naast de coördinaten voor routing en bezorgplanning.
De React-autocomplete-hook bouwen
Begin met het extraheren van de API-logica in een herbruikbare hook. Dat houdt het component overzichtelijk en maakt de hook onafhankelijk testbaar.
// 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 };
}
De debounce-timer wordt pas geactiveerd nadat de gebruiker 300 ms stopt met typen. De MIN_CHARS-beveiliging voorkomt API-aanroepen bij invoer van 1 tot 2 tekens, waarbij resultaten te breed zouden zijn om nuttig te zijn. Beide maatregelen zijn cruciaal om het API-gebruik proportioneel te houden aan de daadwerkelijke gebruikersintenties.
Het autocompletecomponent
// 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>
);
}
Het component verwerkt volledige toetsenbordnavigatie (pijltoetsen, enter, escape), ARIA-attributen voor schermlezercompatibiliteit, en een vertraging van 150 ms bij vervagen zodat muisklikken op suggesties worden geregistreerd voordat de lijst sluit.
Integreren met een checkoutformulier
// 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);
// Coördinaten zijn beschikbaar voor routing/bezorgschatting
console.log('Delivery coordinates:', selected.coordinates);
}
return (
<form>
<h2>Bezorgadres</h2>
<AddressAutocomplete
onSelect={handleAddressSelect}
countryCode="NLD" // Beperkt tot Nederland, verwijder voor EU-breed
placeholder="Begin uw straatnaam in te typen..."
/>
{/* Toon gestructureerde velden na selectie, sta handmatige aanpassingen toe */}
{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="Straat" />
<input value={address.housenumber} onChange={e => setAddress(a => ({ ...a, housenumber: e.target.value }))} placeholder="Nr." style={{ width: 80 }} />
<input value={address.postalcode} onChange={e => setAddress(a => ({ ...a, postalcode: e.target.value }))} placeholder="Postcode" />
<input value={address.locality} onChange={e => setAddress(a => ({ ...a, locality: e.target.value }))} placeholder="Stad" />
</div>
)}
<button type="submit" style={{ marginTop: 16 }}>
Verder naar betaling
</button>
</form>
);
}
Het tonen van afzonderlijke bewerkbare velden na autocomplete is belangrijk voor toegankelijkheid en randgevallen. Het werkelijke deuradres van een gebruiker kan een flatnummer of toegangscode bevatten die het gegeocodeerde resultaat niet bevat. De autocomplete vult het gevalideerde basisadres in; de gebruiker voegt de rest toe.
EU-adresformaatoverwegingen
Verschillende EU-landen hebben adresconventies die zowel de weergave als de volgorde van formuliervelden beïnvloeden:
Duitsland (DEU): Eerst de straat, dan het huisnummer. Hauptstraße 42, 10115 Berlin. De housenumber-eigenschap van de API volgt correct op de straat in Duitse resultaten.
Frankrijk (FRA): Huisnummer voor straat. 42 rue de Rivoli, 75001 Paris. De label-eigenschap retourneert adressen in het landspecifieke formaat.
Nederland (NLD): Nederlandse postcodes zijn 4 cijfers + 2 hoofdletters met een spatie: 1012 LG. Valideer dit formaat als u de postcode splitst voor uw verzendsysteem.
België (BEL): Tweetalige regio's kunnen adressen retourneren in het Frans of Nederlands afhankelijk van de gemeente.
De MapAtlas Geocoding API verwerkt dit alles correct in het label-veld (leesbaar, landspecifiek), en retourneert ook gestructureerde street-, housenumber- en postalcode-velden zodat u landspecifieke formulierindelingen kunt bouwen indien nodig.
Voor bulkvalidatie van bestaande adressenbestanden, bijvoorbeeld het opschonen van een verouderd CRM voordat u een bezorgservice lanceert, zie Hoe u de Geocoding API gebruikt om 10.000 adressen bulksgewijs te valideren.
Prestatieoverwegingen
De bovenstaande implementatie maakt gemiddeld ongeveer één API-aanroep per 3 tot 4 getypte tekens (met de 300 ms debounce die snel typen opvangt). Voor een e-commercesite met noemenswaardige checkoutvolumes, stel een server-side proxy in voor de Geocoding API zodat uw API-sleutel nooit in client-side code verschijnt:
// 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);
}
Werk vervolgens de hook bij om /api/autocomplete aan te roepen in plaats van de MapAtlas API direct. Deze aanpak stelt u ook in staat aanvraagcaching toe te voegen op de edge-laag (Vercel Edge Functions, Cloudflare Workers) om API-aanroepen voor veelvoorkomende queries te verminderen.
Zie de MapAtlas-prijzenpagina voor huidige Geocoding API-tarieven en gratis-tier-limieten. Voor de meeste e-commerceimplementaties past het autocompletegebruik comfortabel binnen de gratis tier tijdens ontwikkeling.
Samenvatting
Een enkel adresautocomplete-veld kan uw checkout-conversieratio aanzienlijk verbeteren. De implementatie is eenvoudig: een debounced fetch naar de MapAtlas Geocoding API, een klein dropdown-component met toetsenbordnavigatie, en formulierintegatie die gestructureerde velden invult vanuit het geselecteerde resultaat.
Belangrijke beslissingen:
- Debounce op 300 ms om buitensporige API-aanroepen bij snel typen te vermijden.
- Vereis 3 tekens voordat requests worden geactiveerd.
- Beperk op land als u de geografie van uw gebruikersbestand kent, dat verbetert de relevantie van resultaten aanzienlijk.
- Sta altijd handmatige bewerking toe van automatisch ingevulde velden voor flatnummers, toegangscodes en correcties.
- Proxy de API-sleutel server-side voor productie om blootstelling van inloggegevens in client-bundles te voorkomen.
Voor uw eerste kaartintegratie naast het adresveld, zie Interactieve kaarten toevoegen aan uw website om de bezorglocatie op een bevestigingspagina te tonen.
Meld u aan voor een gratis MapAtlas API-sleutel om te beginnen. De Geocoding API is inbegrepen in de gratis tier, geen creditcard vereist.

