El campo de dirección en el checkout es donde las conversiones móviles mueren. El usuario llega a tu página de producto, añade un artículo al carrito, avanza al checkout y entonces se le pide que escriba su dirección completa en el teclado táctil de un teléfono de 15 cm. Si escribe mal el código postal, se equivoca con el formato del número de casa o simplemente se rinde, pierdes una venta que ya habías ganado.
El autocompletado de dirección resuelve esto. Después de implementarlo, la entrada de dirección pasa de 15-25 pulsaciones a 3-4. El usuario empieza a escribir el nombre de la calle, ve una sugerencia coincidente en medio segundo, la selecciona y toda la dirección, calle, número, ciudad, código postal, país, se rellena automáticamente y de forma correcta. Las entregas fallidas por errores tipográficos disminuyen. El abandono del checkout disminuye. Y, de forma crucial, obtienes coordenadas geocodificadas y verificadas en cada pedido, que tus sistemas de logística y enrutamiento pueden usar directamente.
La investigación en implementaciones de comercio electrónico muestra consistentemente una mejora del 25-35 % en la tasa de finalización del checkout tras añadir el autocompletado de dirección, con un efecto significativamente mayor en móvil, donde la entrada manual de texto es más lenta y propensa a errores. Algunas implementaciones orientadas a mercados móviles alcanzan el 35 % completo.
Este tutorial construye un componente completo de autocompletado de dirección en React usando la API de Geocodificación de MapAtlas, con debouncing, navegación por teclado, manejo de formatos de dirección europeos e integración en formularios. El componente completo tiene unas 90 líneas.
Por qué los errores de dirección destruyen las conversiones
Las entregas fallidas son costosas en todos los sentidos: el transportista cobra una tasa de reenvío, el equipo de atención al cliente gestiona la reclamación y la confianza del cliente en tu marca sufre un golpe. En el comercio electrónico B2C, los errores en la introducción de direcciones representan aproximadamente el 5-8 % de todas las excepciones de envío.
Las causas subyacentes son predecibles:
- La entrada por teclado móvil produce más errores tipográficos que el escritorio. La corrección automática frecuentemente corrompe nombres de calles y ciudades.
- Los formatos de código postal varían según el país. Un cliente alemán que introduce un código de 5 dígitos en un campo que espera formato del Reino Unido (AN NAA) provocará un error de validación.
- El orden calle/número varía en los países de la UE. En Alemania y los Países Bajos, el número de casa va después del nombre de la calle. En Francia, va antes. Los formularios de entrada manual raramente guían a los usuarios correctamente.
- Las designaciones de piso y apartamento no tienen un formato estándar. Los usuarios los introducen como les parece natural, lo que a menudo no coincide con lo que espera tu transportista.
El autocompletado evita la mayoría de estos problemas al devolver un objeto de dirección pre-validado y estructurado. El usuario selecciona lo que quiere decir, y tu formulario recibe el formato correcto.
El endpoint de autocompletado de la API de Geocodificación de MapAtlas
El endpoint para sugerencias de autocompletado es:
GET https://api.mapatlas.eu/geocoding/v1/autocomplete?text={query}&key={YOUR_API_KEY}
Parámetros opcionales relevantes para el comercio electrónico en la UE:
| Parámetro | Tipo | Descripción |
|---|---|---|
text | string | La consulta de dirección parcial |
focus.point.lon | number | Longitud del usuario (prioriza resultados cercanos) |
focus.point.lat | number | Latitud del usuario (prioriza resultados cercanos) |
boundary.country | string | Código de país ISO 3166-1 alfa-3 (p. ej., DEU, FRA, NLD) |
layers | string | Filtra tipos de resultado: address, street, locality |
size | number | Número de resultados (por defecto 10, máximo 20) |
Una respuesta típica:
{
"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
}
}
]
}
Cada resultado se devuelve como una feature GeoJSON con componentes de dirección estructurados. Tu formulario recibe datos limpios y validados que puedes insertar directamente en cada campo, o almacenar como un único objeto junto con las coordenadas para la planificación de rutas y entregas.
Creando el hook de autocompletado en React
Empieza extrayendo la lógica de la API en un hook reutilizable. Esto mantiene el componente limpio y permite probar el hook de forma independiente.
// 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 };
}
El temporizador de debounce solo se activa tras 300 ms de pausa en la escritura. El control MIN_CHARS evita llamadas a la API con 1-2 caracteres, donde los resultados serían demasiado amplios para ser útiles. Ambas medidas son fundamentales para mantener el uso de la API (y sus costes) proporcional a la intención real del usuario.
El componente de autocompletado
// 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' }}>
Buscando...
</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>
);
}
El componente gestiona la navegación completa por teclado (flechas, enter, escape), atributos ARIA para compatibilidad con lectores de pantalla y un retraso de 150 ms en el blur para que los clics con ratón en las sugerencias se registren antes de que la lista se cierre.
Integración con un formulario de checkout
// 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);
// Las coordenadas están disponibles para enrutamiento y estimación de entrega
console.log('Delivery coordinates:', selected.coordinates);
}
return (
<form>
<h2>Dirección de entrega</h2>
<AddressAutocomplete
onSelect={handleAddressSelect}
countryCode="NLD" // Restringir a Países Bajos, eliminar para toda la UE
placeholder="Empieza a escribir tu dirección..."
/>
{/* Mostrar campos estructurados después de la selección, permitir edición manual */}
{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="Calle" />
<input value={address.housenumber} onChange={e => setAddress(a => ({ ...a, housenumber: e.target.value }))} placeholder="Nº" style={{ width: 80 }} />
<input value={address.postalcode} onChange={e => setAddress(a => ({ ...a, postalcode: e.target.value }))} placeholder="Código postal" />
<input value={address.locality} onChange={e => setAddress(a => ({ ...a, locality: e.target.value }))} placeholder="Ciudad" />
</div>
)}
<button type="submit" style={{ marginTop: 16 }}>
Continuar al pago
</button>
</form>
);
}
Mostrar campos editables individuales después del autocompletado es importante para la accesibilidad y los casos de borde. La dirección real de un usuario puede incluir un número de piso o código de acceso que el resultado geocodificado no incluye. El autocompletado rellena la dirección base validada; el usuario añade el resto.
Consideraciones sobre formatos de dirección en la UE
Los distintos países de la UE tienen convenciones de dirección que afectan tanto a la presentación como al orden de los campos del formulario:
Alemania (DEU): Primero la calle, después el número. Hauptstraße 42, 10115 Berlin. La propiedad housenumber de la API sigue correctamente a la calle en los resultados alemanes.
Francia (FRA): El número va antes de la calle. 42 rue de Rivoli, 75001 Paris. La propiedad label devuelve las direcciones en el formato apropiado para cada país.
Países Bajos (NLD): Los códigos postales neerlandeses son 4 dígitos + 2 letras mayúsculas con un espacio: 1012 LG. Valida este formato si estás dividiendo el código postal para tu sistema de envío.
Bélgica (BEL): Las regiones bilingües pueden devolver direcciones en francés o neerlandés según el municipio.
La API de Geocodificación de MapAtlas gestiona todos estos casos correctamente en el campo label (legible para humanos, con formato apropiado al país) y también devuelve campos estructurados street, housenumber y postalcode para que puedas construir diseños de formulario específicos por país si es necesario.
Para la validación masiva de bases de datos de direcciones existentes, como la limpieza de un CRM heredado antes de lanzar un servicio de entrega, consulta Cómo usar la API de Geocodificación para validar 10.000 direcciones en masa.
Consideraciones de rendimiento
La implementación anterior realiza aproximadamente una llamada a la API por cada 3-4 caracteres escritos de media (con el debounce de 300 ms absorbiendo la escritura rápida). Para un sitio de comercio electrónico con un volumen significativo de checkouts, configura un proxy en el servidor delante de la API de Geocodificación para que tu clave API nunca aparezca en el código del lado del cliente:
// pages/api/autocomplete.js (ruta API de Next.js)
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); // Variable de entorno del servidor
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);
}
Luego actualiza el hook para llamar a /api/autocomplete en lugar de la API de MapAtlas directamente. Este enfoque también permite añadir caché de solicitudes en la capa edge (Vercel Edge Functions, Cloudflare Workers) para reducir las llamadas a la API en consultas frecuentes.
Consulta la página de precios de MapAtlas para conocer las tarifas actuales de la API de Geocodificación y los límites del nivel gratuito; para la mayoría de las implementaciones de comercio electrónico, el uso del autocompletado cabe cómodamente en el nivel gratuito durante el desarrollo.
Resumen
Un único campo de autocompletado de dirección puede mover significativamente tu tasa de conversión en el checkout. La implementación es sencilla: una llamada con debounce a la API de Geocodificación de MapAtlas, un pequeño componente de lista desplegable con navegación por teclado e integración en formularios que rellena campos estructurados a partir del resultado seleccionado.
Decisiones clave:
- Debounce de 300 ms para evitar llamadas excesivas a la API en escritores rápidos.
- Requerir 3 caracteres antes de activar solicitudes.
- Restringir por país si conoces la geografía de tu base de usuarios; mejora significativamente la relevancia de los resultados.
- Permitir siempre la edición manual de los campos autocompletados para números de piso, códigos de acceso y correcciones.
- Pasar la clave API por proxy en el servidor para producción y evitar exponer credenciales en los bundles del cliente.
Para tu primera integración de mapas junto al campo de dirección, consulta Cómo añadir mapas interactivos a tu sitio web para mostrar la ubicación de entrega en una página de confirmación.
Regístrate para obtener una clave API de MapAtlas gratuita y empieza a construir. La API de Geocodificación está incluida en el nivel gratuito, sin tarjeta de crédito requerida.
Preguntas frecuentes
¿Cómo mejora el autocompletado de dirección la conversión en el checkout?
El campo de dirección es el paso de mayor fricción en la mayoría de los flujos de checkout, especialmente en móvil. El autocompletado lo reduce a 2-3 pulsaciones seguidas de un toque, elimina errores de formato que causan entregas fallidas y evita la incertidumbre sobre si la dirección se ingresó correctamente. Los estudios muestran consistentemente una reducción del 25-35 % en el abandono del checkout tras implementar el autocompletado.
¿La API de Geocodificación de MapAtlas soporta formatos de dirección europeos?
Sí. La API maneja formatos específicos de la UE, incluyendo el ordenamiento alemán de número de casa después de la calle, los arrondissements franceses, los códigos postales neerlandeses de 4 dígitos y los formatos de dirección multilingüe en todos los estados miembros de la UE. Los resultados se devuelven como GeoJSON con componentes de dirección estructurados.
¿Cómo evito llamadas excesivas a la API durante el autocompletado?
Aplica un debounce de 250-300 ms al manejador de entrada para enviar una solicitud solo cuando el usuario deja de escribir. Establece también un umbral mínimo de caracteres (3-4) antes de activar las solicitudes. Estas dos medidas reducen las llamadas a la API en aproximadamente un 80 % frente a disparar en cada pulsación.

