Para implementar autocomplete de CPF para usuários recorrentes, armazene no localStorage apenas a versão mascarada do CPF (ex: 123.***.***-09) e um hash de identificação — nunca o número completo. Ao retornar ao site, o usuário vê a sugestão, confirma a identidade e redigita o CPF para revalidação via API. Isso reduz o atrito sem abrir mão da segurança exigida pela LGPD.
Introdução
Usuários recorrentes -- aqueles que voltam ao seu site ou aplicação com frequência -- não deveriam precisar redigitar o CPF a cada nova interação. Um sistema de autocomplete de CPF bem implementado reduz o atrito no preenchimento de formulários, melhora a experiência do usuário e aumenta a velocidade de checkout ou cadastro.
Contudo, o CPF é um dado sensível, e qualquer implementação de autocomplete precisa equilibrar conveniência e segurança.
Autocomplete nativo do navegador
A forma mais simples de autocomplete é utilizar os atributos HTML que permitem ao navegador salvar e sugerir dados:
<div class="form-group">
<label for="cpf">CPF</label>
<input
type="text"
id="cpf"
name="cpf"
autocomplete="off"
inputmode="numeric"
maxlength="14"
placeholder="000.000.000-00"
/>
</div>
Limitações do autocomplete nativo
Para o CPF, o autocomplete nativo do navegador apresenta limitações:
- Sem padrão específico: não existe um valor
autocompleteoficial para CPF. - Formatação inconsistente: o navegador pode salvar com ou sem máscara.
- Sem validação: o dado sugerido não é revalidado automaticamente.
Por isso, uma solução customizada é geralmente mais adequada.
Estratégia 1 -- Autocomplete com localStorage seguro
Armazene uma versão mascarada do CPF no localStorage para exibição, junto com um hash para identificação:
// Módulo de armazenamento seguro de CPF
var CpfStorage = (function () {
var STORAGE_KEY = "cpfhub_saved_cpfs";
var MAX_ENTRIES = 5;
function hashCpf(cpf) {
// Hash simples para identificação (não use para segurança real)
var hash = 0;
for (var i = 0; i < cpf.length; i++) {
var char = cpf.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return hash.toString(36);
}
function maskCpf(cpf) {
// 123.456.789-09 -> 123.***.***-09
var clean = cpf.replace(/\D/g, "");
return clean.slice(0, 3) + ".***.***-" + clean.slice(9, 11);
}
function save(cpf, name) {
var clean = cpf.replace(/\D/g, "");
var entries = getAll();
var existing = entries.findIndex(function (e) {
return e.hash === hashCpf(clean);
});
if (existing >= 0) {
entries[existing].lastUsed = Date.now();
} else {
entries.push({
masked: maskCpf(clean),
hash: hashCpf(clean),
name: name,
lastUsed: Date.now(),
});
}
// Manter apenas as entradas mais recentes
entries.sort(function (a, b) {
return b.lastUsed - a.lastUsed;
});
entries = entries.slice(0, MAX_ENTRIES);
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(entries));
} catch (e) {
// Silenciar erros de quota ou modo privado
}
}
function getAll() {
try {
var raw = localStorage.getItem(STORAGE_KEY);
return raw ? JSON.parse(raw) : [];
} catch (e) {
return [];
}
}
function clear() {
localStorage.removeItem(STORAGE_KEY);
}
return { save: save, getAll: getAll, clear: clear, maskCpf: maskCpf };
})();
Componente de autocomplete dropdown
<div class="cpf-autocomplete" id="cpf-autocomplete">
<div class="cpf-autocomplete__input-wrapper">
<input
type="text"
id="cpf"
class="cpf-autocomplete__input"
inputmode="numeric"
maxlength="14"
placeholder="000.000.000-00"
autocomplete="off"
/>
<div class="cpf-autocomplete__icon" id="cpf-icon"></div>
</div>
<div class="cpf-autocomplete__dropdown" id="cpf-dropdown"></div>
<div class="cpf-autocomplete__feedback" id="cpf-feedback" role="status" aria-live="polite"></div>
</div>
.cpf-autocomplete {
position: relative;
max-width: 400px;
}
.cpf-autocomplete__input-wrapper {
position: relative;
}
.cpf-autocomplete__input {
width: 100%;
padding: 12px 44px 12px 16px;
font-size: 16px;
border: 2px solid #d1d5db;
border-radius: 8px;
outline: none;
transition: border-color 0.2s ease;
}
.cpf-autocomplete__input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1);
}
.cpf-autocomplete__dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 0 0 8px 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
display: none;
z-index: 100;
max-height: 200px;
overflow-y: auto;
}
.cpf-autocomplete__dropdown.visible {
display: block;
}
.cpf-autocomplete__item {
padding: 10px 16px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
transition: background-color 0.15s ease;
}
.cpf-autocomplete__item:hover {
background: #f1f5f9;
}
.cpf-autocomplete__item-cpf {
font-family: monospace;
font-size: 14px;
color: #1e293b;
}
.cpf-autocomplete__item-name {
font-size: 13px;
color: #64748b;
}
.cpf-autocomplete__feedback {
font-size: 13px;
margin-top: 6px;
min-height: 20px;
}
JavaScript do autocomplete
var cpfInput = document.getElementById("cpf");
var dropdown = document.getElementById("cpf-dropdown");
var feedback = document.getElementById("cpf-feedback");
var debounceTimer = null;
// Mostrar sugestões ao focar
cpfInput.addEventListener("focus", function () {
var entries = CpfStorage.getAll();
if (entries.length > 0 && this.value === "") {
showDropdown(entries);
}
});
// Esconder dropdown ao clicar fora
document.addEventListener("click", function (e) {
if (!e.target.closest("#cpf-autocomplete")) {
dropdown.classList.remove("visible");
}
});
function showDropdown(entries) {
dropdown.innerHTML = entries
.map(function (entry, index) {
return (
'<div class="cpf-autocomplete__item" data-index="' + index + '">' +
'<span class="cpf-autocomplete__item-cpf">' + entry.masked + "</span>" +
'<span class="cpf-autocomplete__item-name">' + entry.name + "</span>" +
"</div>"
);
})
.join("");
dropdown.classList.add("visible");
// Eventos de clique nas sugestões
dropdown.querySelectorAll(".cpf-autocomplete__item").forEach(function (item) {
item.addEventListener("click", function () {
// O usuário precisa redigitar -- mostramos apenas para identificação
feedback.textContent =
"Por segurança, por favor redigite o CPF completo.";
feedback.style.color = "#6b7280";
cpfInput.focus();
dropdown.classList.remove("visible");
});
});
}
// Formatação e validação ao digitar
cpfInput.addEventListener("input", function () {
dropdown.classList.remove("visible");
var v = this.value.replace(/\D/g, "").slice(0, 11);
if (v.length > 9)
v = v.replace(/(\d{3})(\d{3})(\d{3})(\d{1,2})/, "$1.$2.$3-$4");
else if (v.length > 6)
v = v.replace(/(\d{3})(\d{3})(\d{1,3})/, "$1.$2.$3");
else if (v.length > 3)
v = v.replace(/(\d{3})(\d{1,3})/, "$1.$2");
this.value = v;
if (debounceTimer) clearTimeout(debounceTimer);
var digits = v.replace(/\D/g, "");
if (digits.length === 11) {
debounceTimer = setTimeout(function () {
validateAndSave(digits);
}, 500);
}
});
async function validateAndSave(cpf) {
feedback.textContent = "Validando...";
feedback.style.color = "#6b7280";
var controller = new AbortController();
var timeoutId = setTimeout(function () {
controller.abort();
}, 10000);
try {
var res = await fetch("https://api.cpfhub.io/cpf/" + cpf, {
headers: {
"x-api-key": "SUA_CHAVE_DE_API",
Accept: "application/json",
},
signal: controller.signal,
});
clearTimeout(timeoutId);
var data = await res.json();
if (data.success) {
feedback.textContent = "CPF válido - " + data.data.name;
feedback.style.color = "#059669";
// Salvar para autocomplete futuro
CpfStorage.save(cpf, data.data.name);
} else {
feedback.textContent = "CPF não encontrado.";
feedback.style.color = "#dc2626";
}
} catch (err) {
clearTimeout(timeoutId);
feedback.textContent = "Erro na validação.";
feedback.style.color = "#dc2626";
}
}
Estratégia 2 -- Autocomplete via conta do usuário
Para plataformas com sistema de login, o autocomplete mais seguro é buscar o CPF da conta do usuário no backend:
// Ao carregar a página, verificar se o usuário está logado
async function prefillCpf() {
try {
var controller = new AbortController();
var timeoutId = setTimeout(function () {
controller.abort();
}, 5000);
var res = await fetch("/api/user/profile", {
credentials: "include",
signal: controller.signal,
});
clearTimeout(timeoutId);
var user = await res.json();
if (user && user.cpf) {
var cpfField = document.getElementById("cpf");
var masked = CpfStorage.maskCpf(user.cpf);
cpfField.value = masked;
cpfField.dataset.prefilled = "true";
var feedback = document.getElementById("cpf-feedback");
feedback.textContent = "CPF da sua conta. Clique para editar.";
feedback.style.color = "#059669";
}
} catch (err) {
// Silenciar -- preenchimento é opcional
}
}
document.addEventListener("DOMContentLoaded", prefillCpf);
Segurança do armazenamento local
O que armazenar
- Nunca armazene o CPF completo no localStorage ou cookies.
- Armazene apenas a versão mascarada (ex:
123.***.***-09) e um hash. - Armazene o nome associado para facilitar a identificação.
Limpeza de dados
Ofereça ao usuário a opção de limpar dados salvos:
<button type="button" class="btn-clear-data" onclick="CpfStorage.clear()">
Limpar CPFs salvos
</button>
Consentimento
Antes de salvar no localStorage, peça consentimento:
function saveCpfWithConsent(cpf, name) {
var consentKey = "cpfhub_storage_consent";
if (localStorage.getItem(consentKey) === "true") {
CpfStorage.save(cpf, name);
return;
}
if (
confirm(
"Deseja salvar este CPF para preenchimento automático em futuras visitas?"
)
) {
localStorage.setItem(consentKey, "true");
CpfStorage.save(cpf, name);
}
}
Navegação por teclado no dropdown
Para acessibilidade, o dropdown deve ser navegável por teclado:
cpfInput.addEventListener("keydown", function (e) {
var items = dropdown.querySelectorAll(".cpf-autocomplete__item");
if (!items.length || !dropdown.classList.contains("visible")) return;
var active = dropdown.querySelector(".cpf-autocomplete__item--active");
var index = active ? Array.from(items).indexOf(active) : -1;
if (e.key === "ArrowDown") {
e.preventDefault();
if (active) active.classList.remove("cpf-autocomplete__item--active");
index = (index + 1) % items.length;
items[index].classList.add("cpf-autocomplete__item--active");
} else if (e.key === "ArrowUp") {
e.preventDefault();
if (active) active.classList.remove("cpf-autocomplete__item--active");
index = (index - 1 + items.length) % items.length;
items[index].classList.add("cpf-autocomplete__item--active");
} else if (e.key === "Enter" && active) {
e.preventDefault();
active.click();
} else if (e.key === "Escape") {
dropdown.classList.remove("visible");
}
});
Perguntas frequentes
É seguro salvar CPF no localStorage para autocomplete?
Desde que você armazene apenas a versão mascarada (ex: 123.***.***-09) e um hash — nunca o CPF completo — o risco é baixo. O localStorage é acessível apenas pelo mesmo domínio, mas não é criptografado, então evite dados completos. A ANPD orienta que dados pessoais devem ser tratados com o princípio da minimização: colete e armazene apenas o estritamente necessário.
Por que o usuário precisa redigitar o CPF mesmo com autocomplete?
A sugestão serve para identificar qual CPF usar, não para preencher automaticamente. Exigir a redigitação garante que o titular está presente e consciente da ação — o que é especialmente relevante em dispositivos compartilhados e em fluxos regulados pela LGPD.
Como implementar autocomplete de CPF em SPAs com React ou Vue?
Encapsule o CpfStorage em um hook customizado (useCpfAutocomplete) ou composable Vue. Use useEffect/onMounted para carregar as sugestões do localStorage ao montar o componente e chame a API CPFHub.io com debounce de 500ms após o usuário digitar os 11 dígitos.
O que fazer se o usuário estiver em modo de navegação privada?
O localStorage não persiste em modo privado. Trate o erro silenciosamente no bloco try/catch do CpfStorage.save() — o formulário continua funcionando normalmente, só sem a sugestão de autocomplete. Para usuários logados, a estratégia via backend (Estratégia 2) funciona independentemente do modo de navegação.
Conclusão
O autocomplete de CPF para usuários recorrentes melhora a experiência em formulários de uso frequente. A chave é equilibrar conveniência e segurança -- nunca armazenando o CPF completo, sempre pedindo consentimento e revalidando o dado quando necessário. A CPFHub.io oferece validação em ~900ms, uptime de 99,9% e conformidade total com a LGPD, garantindo que cada consulta seja segura e confiável.
Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito.
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
Sobre a redação
Redação CPFHub.io
Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.



