Skip to main content
Autocomplete de endereço no checkout React: como um campo reduz
Tutorials

Autocomplete de endereço no checkout React: como um campo reduz

Adicione autocomplete de endereço ao checkout React com a API de Geocodificação MapAtlas. Reduza abandono de carrinho

Brent van der Heiden9 min read
#address autocomplete#geocoding api#checkout conversion#address validation#javascript autocomplete

O campo de endereço no checkout é onde as conversões móveis morrem. O usuário navega até a página do produto, adiciona um item ao carrinho, avança para o checkout e então é solicitado a digitar o endereço completo num teclado touchscreen de 15 cm. Se errar o código postal, errar o formato do número da casa, ou simplesmente desistir, você perde uma venda que já estava ganha.

O autocompletar de endereço resolve isso. Após a implementação, o preenchimento do endereço cai de 15-25 toques para 3-4. O usuário começa a digitar o nome da rua, vê uma sugestão correspondente em meio segundo, toca nela, e o endereço completo, rua, número, cidade, código postal, país, é preenchido automaticamente e corretamente. Falhas nas entregas causadas por erros de digitação diminuem. O abandono no checkout diminui. E, crucialmente, você tem coordenadas geocodificadas e verificadas associadas a cada pedido, que seus sistemas de logística e roteamento podem usar diretamente.

Pesquisas em implementações de e-commerce mostram consistentemente melhorias de 25-35% nas taxas de conclusão do checkout após a adição do autocompletar de endereço, com efeito significativamente mais forte em dispositivos móveis, onde a digitação manual é mais lenta e propensa a erros. Algumas implementações voltadas para mercados mobile-first reportam os 35% completos.

Este tutorial cria um componente React completo de autocompletar de endereço usando a API de Geocodificação MapAtlas, incluindo debouncing, navegação por teclado, tratamento de formatos de endereço europeus e integração de formulário. O componente completo tem cerca de 90 linhas.

Por que Erros de Endereço Destroem Conversões

Entregas com falha são caras em todos os sentidos: a transportadora cobra uma taxa de reentrega, sua equipe de atendimento ao cliente lida com a reclamação, e a confiança do cliente na sua marca é afetada. No e-commerce B2C, erros na entrada de endereço representam aproximadamente 5-8% de todas as exceções de envio.

As causas subjacentes são previsíveis:

  • Digitação no teclado móvel produz mais erros do que no desktop. A correção automática frequentemente corrompe nomes de ruas e cidades.
  • Formatos de endereço nacionais variam muito na Europa. Alemanha: Straße antes do número. França: número antes da rua. Países Baixos: códigos postais de 4 dígitos com sufixo de 2 letras. Os usuários não sabem qual formato você espera.
  • Campos de formulário longos sem validação ao vivo deixam os erros passarem pela confirmação do pedido. O usuário fica satisfeito. A falha na entrega acontece depois.
  • Preenchimento automático do navegador muitas vezes insere endereços antigos ou desatualizados que o usuário não percebe.

O autocompletar de endereço por API elimina a maioria dessas categorias de erro na origem. O usuário seleciona de uma lista de sugestões verificadas, não digita de memória.

Configurando a API de Geocodificação MapAtlas

Antes de construir o componente, você precisa de uma chave de API. Cadastre-se em portal.mapmetrics.org/signup. O plano gratuito inclui 10.000 requisições por mês, mais do que suficiente para desenvolvimento e aplicações de pequeno porte.

A API de Geocodificação aceita uma string de consulta e retorna features GeoJSON com endereços estruturados:

GET https://api.mapatlas.eu/geocoding/v1/autocomplete?text=Herengracht&key=YOUR_API_KEY&lang=pt&country=NL

Parâmetros relevantes para checkout:

  • text: a string digitada pelo usuário
  • lang: código de idioma para rótulos de sugestões localizados
  • country: restringe os resultados a um país (ISO 3166-1 alfa-2), reduz resultados irrelevantes drasticamente para checkouts de país único
  • size: número de sugestões a retornar (o padrão é 10; use 5 para dropdowns de checkout)

A resposta retorna features com geometry.coordinates (lat/lng) e properties contendo os campos de endereço estruturados.

O Componente React

O componente lida com toda a experiência: o campo de entrada, o dropdown de sugestões, a navegação por teclado e o preenchimento do formulário ao selecionar.

import { useState, useRef, useCallback } from 'react';

const GEOCODING_URL = 'https://api.mapatlas.eu/geocoding/v1/autocomplete';
const API_KEY = process.env.NEXT_PUBLIC_MAPATLAS_KEY;

export function AddressAutocomplete({ onSelect, country = 'NL' }) {
  const [query, setQuery]           = useState('');
  const [suggestions, setSuggestions] = useState([]);
  const [activeIndex, setActiveIndex] = useState(-1);
  const [isOpen, setIsOpen]         = useState(false);
  const debounceRef                 = useRef(null);

  const fetchSuggestions = useCallback(async (text) => {
    if (text.length < 3) { setSuggestions([]); return; }

    const url = new URL(GEOCODING_URL);
    url.searchParams.set('text', text);
    url.searchParams.set('key', API_KEY);
    url.searchParams.set('size', '5');
    url.searchParams.set('country', country);

    try {
      const res  = await fetch(url);
      const data = await res.json();
      setSuggestions(data.features || []);
      setIsOpen(true);
      setActiveIndex(-1);
    } catch {
      setSuggestions([]);
    }
  }, [country]);

  const handleChange = (e) => {
    const value = e.target.value;
    setQuery(value);
    clearTimeout(debounceRef.current);
    debounceRef.current = setTimeout(() => fetchSuggestions(value), 250);
  };

  const handleSelect = (feature) => {
    const p = feature.properties;
    onSelect({
      street:     `${p.street || ''} ${p.housenumber || ''}`.trim(),
      city:       p.city || p.locality || '',
      postalCode: p.postalcode || '',
      country:    p.country_code?.toUpperCase() || country,
      lat:        feature.geometry.coordinates[1],
      lng:        feature.geometry.coordinates[0],
    });
    setQuery(p.label || '');
    setIsOpen(false);
  };

  const handleKeyDown = (e) => {
    if (!isOpen) return;
    if (e.key === 'ArrowDown')  setActiveIndex(i => Math.min(i + 1, suggestions.length - 1));
    if (e.key === 'ArrowUp')    setActiveIndex(i => Math.max(i - 1, 0));
    if (e.key === 'Enter' && activeIndex >= 0) { e.preventDefault(); handleSelect(suggestions[activeIndex]); }
    if (e.key === 'Escape')     setIsOpen(false);
  };

  return (
    <div style={{ position: 'relative' }}>
      <input
        type="text"
        value={query}
        onChange={handleChange}
        onKeyDown={handleKeyDown}
        onBlur={() => setTimeout(() => setIsOpen(false), 150)}
        placeholder="Comece a digitar seu endereço..."
        autoComplete="off"
        style={{ width: '100%', padding: '10px 12px', fontSize: '16px',
                 border: '1px solid #d1d5db', borderRadius: '6px' }}
      />
      {isOpen && suggestions.length > 0 && (
        <ul style={{
          position: 'absolute', top: '100%', left: 0, right: 0,
          background: '#fff', border: '1px solid #d1d5db', borderRadius: '6px',
          listStyle: 'none', margin: '4px 0 0', padding: 0,
          boxShadow: '0 4px 16px rgba(0,0,0,0.12)', zIndex: 100,
        }}>
          {suggestions.map((f, i) => (
            <li
              key={f.properties.osm_id || i}
              onMouseDown={() => handleSelect(f)}
              style={{
                padding: '10px 12px', cursor: 'pointer', fontSize: '14px',
                background: i === activeIndex ? '#EFF6FF' : 'transparent',
              }}
            >
              {f.properties.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

Integrando com o Formulário de Checkout

O componente chama onSelect com campos de endereço estruturados. Conecte-o ao estado do formulário de checkout:

import { useState } from 'react';
import { AddressAutocomplete } from './AddressAutocomplete';

export function CheckoutForm() {
  const [address, setAddress] = useState({
    street: '', city: '', postalCode: '', country: 'NL', lat: null, lng: null,
  });
  const [submitted, setSubmitted] = useState(false);

  const handleAddressSelect = (selectedAddress) => {
    setAddress(selectedAddress);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    // address.lat e address.lng estão disponíveis para uso imediato em sistemas de logística
    console.log('Dados de endereço:', address);
    setSubmitted(true);
  };

  return (
    <form onSubmit={handleSubmit} style={{ maxWidth: '480px', margin: '0 auto' }}>
      <h2>Endereço de Entrega</h2>

      <label style={{ display: 'block', marginBottom: '16px' }}>
        <span style={{ fontSize: '14px', fontWeight: 600, color: '#374151' }}>
          Endereço
        </span>
        <AddressAutocomplete
          onSelect={handleAddressSelect}
          country="NL"
        />
      </label>

      {address.street && (
        <div style={{ background: '#F9FAFB', borderRadius: '8px', padding: '12px', marginBottom: '16px' }}>
          <p style={{ margin: '0 0 4px', fontWeight: 600 }}>{address.street}</p>
          <p style={{ margin: '0 0 4px', color: '#6B7280' }}>{address.postalCode} {address.city}</p>
          <p style={{ margin: 0, color: '#6B7280' }}>{address.country}</p>
        </div>
      )}

      <button
        type="submit"
        disabled={!address.street}
        style={{
          width: '100%', padding: '12px', background: '#2563EB', color: '#fff',
          border: 'none', borderRadius: '6px', fontSize: '16px', fontWeight: 600,
          cursor: address.street ? 'pointer' : 'not-allowed', opacity: address.street ? 1 : 0.5,
        }}
      >
        Continuar para Pagamento
      </button>

      {submitted && <p style={{ color: 'green', marginTop: '8px' }}>Pedido enviado.</p>}
    </form>
  );
}

O campo lat/lng no objeto address é passado diretamente para a API de Roteamento MapAtlas para otimização de entrega, sem chamada de geocodificação adicional no momento do pedido.

Debouncing e Rate Limiting

O componente usa um debounce de 250ms, o que significa que a API só é chamada quando o usuário para de digitar por um quarto de segundo. Para um checkout ativo durante uma promoção, isso pode representar a diferença entre custos de API gerenciáveis e uma fatura inesperada.

Para aplicações de alto tráfego, faça a chamada geocoding passar pelo seu backend:

// Rota Next.js API: pages/api/geocode.js ou app/api/geocode/route.js
export async function GET(request) {
  const { searchParams } = new URL(request.url);
  const text    = searchParams.get('text');
  const country = searchParams.get('country') || 'NL';

  const url = new URL('https://api.mapatlas.eu/geocoding/v1/autocomplete');
  url.searchParams.set('text', text);
  url.searchParams.set('key', process.env.MAPATLAS_KEY); // chave do servidor, não exposta ao cliente
  url.searchParams.set('size', '5');
  url.searchParams.set('country', country);

  const res  = await fetch(url);
  const data = await res.json();
  return Response.json(data);
}

Esta abordagem também mantém sua chave de API fora do código do lado do cliente e permite cache no servidor de resultados frequentes.

Formatos de Endereço Europeus

A API de Geocodificação MapAtlas lida com formatos de endereço por país automaticamente, mas há algumas particularidades que valem a pena conhecer:

Países Baixos: Os códigos postais holandeses têm o formato 1234 AB. A API retorna ambas as partes no campo postalcode. Seu formulário deve aceitar código postal com e sem espaço.

Alemanha: As ruas alemãs frequentemente ficam antes do número da casa na formatação local, mas a API normaliza para o formato Strasse Hausnummer.

França: O arrondissement de Paris é incluído na localidade, por exemplo, "Paris 8e Arrondissement". Se você quiser mostrar apenas "Paris", filtre pelo campo city em vez de locality.

UK: Os postcodes britânicos têm dois segmentos. A API retorna o postcode completo como SW1A 1AA. Certifique-se de que o campo do formulário tenha espaço suficiente.

Para casos de uso de checkout, passar o parâmetro country específico do usuário (por exemplo, com base na seleção de país de envio) melhora significativamente a relevância das sugestões.

O que Vem Depois


Perguntas frequentes

Por que usar a API de Geocodificação em vez do preenchimento automático do navegador?

O preenchimento automático do navegador usa o histórico de entrada do usuário, que pode estar desatualizado, em formato incorreto ou pertencer a um endereço diferente. A API de Geocodificação retorna endereços verificados e atuais com coordenadas precisas, formatos normalizados para cada país, e garante que cada pedido tenha um endereço de entrega válido e localizável.

O componente funciona em aplicações que não sejam React?

A lógica central, a chamada fetch, o debouncing e a construção do dropdown, são JavaScript vanilla. O exemplo do React é o padrão mais comum, mas o mesmo padrão pode ser adaptado para Vue, Svelte, ou JavaScript puro em qualquer renderização HTML.

Como restrinjo sugestões a uma área específica?

Use o parâmetro country para restringir por país, e o parâmetro bbox (bounding box) para restringir a uma área geográfica: &bbox=minLng,minLat,maxLng,maxLat. Isso é útil para checkout de entrega local onde você serve apenas uma região.

Perguntas frequentes

Como o autocomplete de endereço melhora a conversão no checkout?

A entrada de endereço é a etapa de maior atrito na maioria dos fluxos de checkout, especialmente no mobile. O autocomplete reduz isso a 2-3 teclas seguidas de um toque, elimina erros de formato que causam falhas na entrega e remove a ansiedade de saber se o endereço foi digitado corretamente. Estudos mostram consistentemente uma redução de 25-35% no abandono de checkout após a implementação do autocomplete.

A API de Geocodificação MapAtlas suporta formatos de endereço europeus?

Sim. A API lida com formatos específicos da UE, incluindo a ordenação de número de casa após o nome da rua na Alemanha, os arrondissements franceses, os códigos postais holandeses de 4 dígitos e os formatos de endereço multilíngues em todos os estados membros da UE. Os resultados são retornados como GeoJSON com componentes de endereço estruturados.

Como evitar chamadas excessivas à API durante o autocomplete?

Faça debounce do handler de input em 250-300ms para só enviar uma requisição após o usuário parar de digitar. Defina também um limite mínimo de caracteres (3-4 caracteres) antes de disparar as requisições. Essas duas medidas reduzem as chamadas à API em aproximadamente 80% em comparação com o disparo a cada tecla pressionada.

Achou útil? Compartilhe.

Sobre o autor

Brent van der Heiden

Escrito por

Brent van der Heiden

Co-Founder & CEO at MapAtlas

Brent built MapAtlas out of a conviction that developers deserve location APIs with fair pricing and genuine end-user privacy. He writes about geospatial infrastructure, AI search visibility, and how location data powers the products people rely on every day.

Ver todos os artigos
Voltar ao blog