Design system: como padronizar o componente de input de CPF na sua empresa

Aprenda a criar um componente reutilizável de input de CPF para seu design system. Padronize validação, máscara e integração com API em toda a empresa.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Design system: como padronizar o componente de input de CPF na sua empresa

Padronizar o componente de input de CPF no design system elimina implementações inconsistentes entre equipes, centraliza a validação (sintática e via API) em um único lugar e garante que qualquer correção se propague automaticamente para todos os produtos da empresa. Um componente bem especificado inclui cinco estados visuais, suporte a validação via API e tokens de design para bordas e mensagens de erro.

Introdução

Em empresas com múltiplos produtos e equipes de desenvolvimento, o campo de CPF aparece em dezenas de formulários diferentes: cadastro de clientes, checkout, onboarding de funcionários, emissão de notas fiscais. Sem um componente padronizado, cada equipe implementa o campo à sua maneira -- com validações diferentes, máscaras inconsistentes e mensagens de erro que variam de um produto para outro.

Quando combinado com uma API como a CPFHub.io, o componente passa a fazer validação real dos dados cadastrais — nome e data de nascimento — além da validação sintática dos dígitos verificadores. Essa combinação elimina cadastros com CPFs inválidos ou pertencentes a terceiros.


Por que padronizar o input de CPF no design system

Um design system é uma coleção de componentes reutilizáveis, padrões e diretrizes que garantem consistência visual e funcional em todos os produtos de uma empresa. O input de CPF é um dos componentes mais importantes para empresas brasileiras, e padronizá-lo traz benefícios concretos:

  • Consistência de UX -- O usuário tem a mesma experiência ao informar o CPF em qualquer produto da empresa.

  • Manutenção centralizada -- Correções de bugs e melhorias são aplicadas uma vez e propagadas automaticamente.

  • Redução de retrabalho -- Equipes não precisam reimplementar validação, máscara e mensagens de erro.

  • Qualidade garantida -- Testes unitários e de acessibilidade são escritos uma vez para o componente compartilhado.

  • Integração padronizada -- A chamada à API de validação segue o mesmo padrão em todos os contextos.


Especificação do componente

Antes de codificar, documente a especificação do componente no design system.

Props (propriedades)

PropTipoDefaultDescrição
valuestring""Valor atual do campo
onChangefunction-Callback quando o valor muda
onValidatefunction-Callback quando a validação é concluída
validateOnBlurbooleantrueSe deve validar ao sair do campo
validateViaAPIbooleanfalseSe deve consultar a API para validação real
apiKeystring-Chave da API (obrigatório se validateViaAPI=true)
disabledbooleanfalseSe o campo está desabilitado
helperTextstring""Texto auxiliar abaixo do campo
requiredbooleanfalseSe o campo é obrigatório

Estados visuais

O componente deve suportar cinco estados visuais:

  • Default -- Campo vazio, borda neutra.

  • Focused -- Campo em foco, borda azul.

  • Loading -- Validação via API em andamento, ícone de loading.

  • Success -- CPF validado, borda verde com ícone de check.

  • Error -- Validação falhou, borda vermelha com mensagem de erro.


Implementação do componente em React

import { useState, useCallback } from 'react';

function InputCPF({
    value = '',
    onChange,
    onValidate,
    validateOnBlur = true,
    validateViaAPI = false,
    apiKey = '',
    disabled = false,
    helperText = '',
    required = false,
}) {
    const [status, setStatus] = useState('default');
    const [mensagem, setMensagem] = useState('');

    const aplicarMascara = (valor) => {
    const numeros = valor.replace(/\D/g, '').slice(0, 11);
    if (numeros.length <= 3) return numeros;
    if (numeros.length <= 6) return `${numeros.slice(0, 3)}.${numeros.slice(3)}`;
    if (numeros.length <= 9)
    return `${numeros.slice(0, 3)}.${numeros.slice(3, 6)}.${numeros.slice(6)}`;
    return `${numeros.slice(0, 3)}.${numeros.slice(3, 6)}.${numeros.slice(6, 9)}-${numeros.slice(9)}`;
    };

    const validarSintatico = (cpf) => {
    const limpo = cpf.replace(/\D/g, '');
    if (limpo.length !== 11) return { valido: false, erro: 'O CPF precisa ter 11 dígitos.' };
    if (/^(\d)\1{10}$/.test(limpo)) return { valido: false, erro: 'CPF inválido.' };

    let soma = 0;
    for (let i = 0; i < 9; i++) soma += parseInt(limpo[i]) * (10 - i);
    let resto = (soma * 10) % 11;
    if (resto === 10) resto = 0;
    if (resto !== parseInt(limpo[9])) return { valido: false, erro: 'Dígitos verificadores incorretos.' };

    soma = 0;
    for (let i = 0; i < 10; i++) soma += parseInt(limpo[i]) * (11 - i);
    resto = (soma * 10) % 11;
    if (resto === 10) resto = 0;
    if (resto !== parseInt(limpo[10])) return { valido: false, erro: 'Dígitos verificadores incorretos.' };

    return { valido: true };
    };

    const validarViaAPI = useCallback(async (cpf) => {
    const limpo = cpf.replace(/\D/g, '');
    setStatus('loading');
    setMensagem('Verificando CPF...');

    try {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 10000);

    const response = await fetch(
    `https://api.cpfhub.io/cpf/${limpo}`,
    {
    headers: {
    'x-api-key': apiKey,
    'Accept': 'application/json'
    },
    signal: controller.signal
    }
    );

    clearTimeout(timeout);
    const resultado = await response.json();

    if (resultado.success) {
    setStatus('success');
    setMensagem('CPF verificado com sucesso.');
    onValidate?.({ valido: true, dados: resultado.data });
    } else {
    setStatus('error');
    setMensagem('CPF não encontrado na base.');
    onValidate?.({ valido: false });
    }
    } catch {
    setStatus('error');
    setMensagem('Erro na verificação. Tente novamente.');
    onValidate?.({ valido: false });
    }
    }, [apiKey, onValidate]);

    const handleBlur = () => {
    if (!validateOnBlur) return;

    const resultado = validarSintatico(value);
    if (!resultado.valido) {
    setStatus('error');
    setMensagem(resultado.erro);
    onValidate?.({ valido: false });
    return;
    }

    if (validateViaAPI) {
    validarViaAPI(value);
    } else {
    setStatus('success');
    setMensagem('Formato válido.');
    onValidate?.({ valido: true });
    }
    };

    return (
    <div className={`input-cpf input-cpf--${status}`}>
    <label className="input-cpf__label">
    CPF {required && <span className="input-cpf__required">*</span>}
    </label>
    <input
    type="text"
    inputMode="numeric"
    placeholder="000.000.000-00"
    value={aplicarMascara(value)}
    onChange={(e) => {
    setStatus('default');
    setMensagem('');
    onChange?.(e.target.value.replace(/\D/g, ''));
    }}
    onBlur={handleBlur}
    disabled={disabled}
    aria-invalid={status === 'error'}
    aria-describedby="cpf-helper"
    />
    <span id="cpf-helper" className="input-cpf__helper">
    {mensagem || helperText}
    </span>
    </div>
    );
}

export default InputCPF;

Tokens de design para o componente

No design system, defina tokens específicos para cada estado visual do componente:

:root {
    /* Cores de borda por estado */
    --input-cpf-border-default: #d1d5db;
    --input-cpf-border-focus: #3b82f6;
    --input-cpf-border-success: #22c55e;
    --input-cpf-border-error: #ef4444;

    /* Cores de texto de mensagem */
    --input-cpf-text-helper: #6b7280;
    --input-cpf-text-success: #16a34a;
    --input-cpf-text-error: #dc2626;

    /* Espaçamentos */
    --input-cpf-padding: 12px;
    --input-cpf-gap: 4px;
    --input-cpf-border-radius: 6px;
}

Esses tokens garantem que qualquer mudança visual (como atualização de cores da marca) se propague automaticamente para todos os inputs de CPF na empresa.


Documentação no design system

O componente deve ter uma página dedicada na documentação do design system com:

  • Exemplos interativos -- Demonstrações de cada estado visual.

  • Tabela de props -- Todas as propriedades com tipos, defaults e descrições.

  • Diretrizes de uso -- Quando usar validação local vs. validação via API.

  • Acessibilidade -- Checklist de requisitos ARIA e navegação por teclado. O guia WCAG do W3C define os critérios mínimos de acessibilidade para campos de formulário.

  • Código de exemplo -- Snippet para copiar e usar em cada framework suportado.


Testes do componente

Inclua testes unitários que cubram os cenários principais:

  • Máscara -- Verifica se o valor "12345678900" é exibido como "123.456.789-00".

  • Validação sintática -- Testa CPFs válidos e inválidos.

  • Validação via API -- Usa mocks para simular respostas de sucesso e erro.

  • Estados visuais -- Verifica se as classes CSS corretas são aplicadas em cada estado.

  • Acessibilidade -- Confirma que aria-invalid e aria-describedby estão presentes.


Perguntas frequentes

Quando usar validação sintática versus validação via API no componente?

Use validação sintática (dígitos verificadores) no onChange ou onBlur para feedback imediato e sem custo de API. Ative validateViaAPI apenas em contextos críticos — como cadastro de conta ou checkout — onde é necessário confirmar que o CPF pertence a uma pessoa real e que o nome informado corresponde ao titular.

Como evitar chamadas desnecessárias à API quando o usuário ainda está digitando?

Ative a validação via API somente no evento onBlur (saída do campo), não no onChange. Isso garante que a chamada ocorra apenas quando o usuário terminar de digitar, economizando consultas e melhorando a experiência.

A API bloqueia requisições quando o limite de consultas é atingido?

Não. A API da CPFHub.io não retorna erro de bloqueio ao atingir o limite do plano. Quando o limite gratuito (50 consultas/mês) é superado, cada consulta adicional é cobrada a R$0,15 automaticamente. Para componentes com alto volume de validações, o plano Pro oferece 1.000 consultas mensais por R$149.

Como tratar a prop apiKey com segurança no componente React?

A apiKey nunca deve ser passada diretamente do front-end. O componente deve receber a chave via prop injetada por um backend (BFF ou API route), ou a chamada à API deve ser delegada a um endpoint server-side. Expor a chave no bundle JavaScript permite que qualquer usuário extraia e abuse dela.


Conclusão

Padronizar o componente de input de CPF no design system é um investimento que paga dividendos em consistência, qualidade e produtividade. Com um componente único e bem documentado, todas as equipes da empresa compartilham a mesma validação, a mesma experiência visual e a mesma integração com API.

A CPFHub.io oferece a API de validação cadastral que alimenta o componente — retornando nome, gênero e data de nascimento em tempo real, com plano gratuito sem cartão de crédito.

Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e integre validação real de CPF ao componente do seu design system.

CPFHub.io

Pronto para integrar a API?

50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.

Redação CPFHub.io

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.

WhatsAppFale conosco via WhatsApp