# 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.

**Publicado:** 08/11/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/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**](https://www.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)

| Prop | Tipo | Default | Descrição |
| --- | --- | --- | --- |
| value | string | "" | Valor atual do campo |
| onChange | function | - | Callback quando o valor muda |
| onValidate | function | - | Callback quando a validação é concluída |
| validateOnBlur | boolean | true | Se deve validar ao sair do campo |
| validateViaAPI | boolean | false | Se deve consultar a API para validação real |
| apiKey | string | - | Chave da API (obrigatório se validateViaAPI=true) |
| disabled | boolean | false | Se o campo está desabilitado |
| helperText | string | "" | Texto auxiliar abaixo do campo |
| required | boolean | false | Se 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

```javascript
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:

```css
: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](https://www.w3.org/WAI/WCAG21/quickref/) 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.

### Leia também

- [Como criar um componente reutilizável de input de CPF para design systems](https://cpfhub.io/blog/como-criar-um-componente-reutilizavel-de-input-de-cpf-para-design-systems)
- [Como pedir CPF no checkout sem espantar o cliente](https://cpfhub.io/blog/como-pedir-cpf-no-checkout-sem-espantar-o-cliente)
- [Como evitar chargebacks usando validação de CPF no checkout](https://cpfhub.io/blog/como-evitar-chargebacks-usando-validacao-de-cpf-no-checkout)
- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)

---

## 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**](https://www.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](https://www.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.

