Het adresveld in de checkout is waar mobiele conversies verloren gaan. Een gebruiker navigeert naar je 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, ben je een verkoop kwijt die je al had gewonnen.
Adresautocomplete lost dit op. Na implementatie gaat adresinvoer van 25 of meer tekens typen verspreid over meerdere velden naar een paar toetsaanslagen en een tap. 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 je hebt geverifieerde, gegeocodeerde coördinaten bij elke bestelling, die je logistieke en routesystemen direct kunnen gebruiken.
Het Baymard Institute schat de winst van een beter checkoutontwerp op een conversiestijging van 35,26% voor de gemiddelde grote e-commercesite, en uit de benchmark bleek dat 55% van de sites nog geen volledig automatische adreszoekfunctie heeft. Daarmee is het adresveld een van de makkelijkste plekken om te beginnen. Mobiele shoppers merken het meest, omdat typen op een telefoontoetsenbord traag en foutgevoelig is.
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, je klantenserviceteam handelt de klacht af, en het vertrouwen van de klant in je merk krijgt een deuk. In de grootschalige checkouttests van het Baymard Institute bevatte 9% van de handmatig ingevoerde verzendadressen typ- of spelfouten.
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 je 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 je formulier ontvangt het correcte formaat.
Het MapAtlas Geocoding Autocomplete-eindpunt
Het eindpunt voor autocompletesuggesties is:
GET https://gateway.mapmetrics-atlas.net/autocomplete/?text={query}&token={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-2 landcode (bijv. DE, FR, NL) |
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. Je formulier ontvangt schone, gevalideerde gegevens die je 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://gateway.mapmetrics-atlas.net/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('token', 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="NL" // 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 je de postcode splitst voor je 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 je landspecifieke formulierindelingen kunt bouwen indien nodig.
Voor bulkvalidatie van bestaande adressenbestanden, bijvoorbeeld het opschonen van een verouderd CRM voordat je een bezorgservice lanceert, zie Hoe je de Geocoding API gebruikt om 10.000 adressen bulksgewijs te valideren.
Prestatieoverwegingen
De bovenstaande implementatie stuurt één API-aanroep telkens wanneer de gebruiker 300 ms pauzeert met typen, in plaats van één per teken, dus een typisch adres kost een handvol requests. Voor een e-commercesite met noemenswaardige checkoutvolumes, stel een server-side proxy in voor de Geocoding API zodat je 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://gateway.mapmetrics-atlas.net/autocomplete/');
url.searchParams.set('text', text);
url.searchParams.set('token', 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 je 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 je 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 je de geografie van je 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 je eerste kaartintegratie naast het adresveld, zie Interactieve kaarten toevoegen aan je website om de bezorglocatie op een bevestigingspagina te tonen.
Meld je aan voor een gratis MapAtlas API-sleutel om te beginnen. De Geocoding API is inbegrepen in de gratis tier, geen creditcard vereist.
Veelgestelde vragen
Hoe verbetert adres autocomplete de checkout-conversie?
Adresinvoer is een van de onderdelen van de checkout waarbij het meest getypt moet worden, zeker op mobiel. Autocomplete brengt het terug tot een paar toetsaanslagen plus een tap, vangt formaatfouten af die mislukte leveringen veroorzaken en neemt de twijfel weg of het adres correct is ingevoerd. In de grootschalige checkouttests van het Baymard Institute bevatte 9% van de handmatig ingevoerde verzendadressen typ- of spelfouten, precies het soort fout dat een gekozen suggestie voorkomt.
Ondersteunt de MapAtlas Geocoding API Europese adresformaten?
Ja. De API verwerkt EU-specifieke formaten, waaronder het Duitse huisnummer-na-straatnaam, Franse arrondissements, Nederlandse postcodes (vier cijfers plus twee letters) en meertalige adresformaten voor alle EU-lidstaten. Resultaten worden geretourneerd als GeoJSON met gestructureerde adrescomponenten.
Hoe voorkom ik overmatige API-aanroepen tijdens autocomplete?
Debounce de inputhandler met 250-300ms zodat je alleen een request stuurt nadat de gebruiker stopt met typen. Stel ook een minimum tekendrempel in (3-4 tekens) voor je requests triggert. Samen verlagen deze twee maatregelen het aantal requests flink vergeleken met vuren op elke toetsaanslag: een adres van 20 tekens dat in een paar reeksen wordt getypt, stuurt een handvol requests in plaats van 20.

