# Como criar um formulário de cadastro com validação de CPF em Next.js

> Crie um formulário de cadastro completo com validação de CPF em Next.js usando React Hook Form e consulta à API do CPFHub.

**Publicado:** 06/12/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-criar-formulario-cadastro-validacao-cpf-nextjs

---


Para criar um formulário de cadastro com validação de CPF em Next.js, combine React Hook Form para gerenciamento de estado, Zod para validação de schema e um Route Handler que consulta a API da CPFHub.io no servidor. Essa arquitetura garante que a API key nunca fica exposta no cliente e que o nome e a data de nascimento do titular são preenchidos automaticamente após a validação. O resultado é um formulário que reduz erros de digitação e melhora a experiência do usuário.

## Introdução

Formulários de cadastro são um dos pontos mais críticos de qualquer aplicação. Validar o CPF durante o preenchimento, com feedback visual e preenchimento automático de dados, melhora drasticamente a experiência do usuário e a qualidade dos dados coletados. Este guia mostra como construir esse formulário em Next.js com React Hook Form, validação de CPF no cliente e no servidor, e integração com a API do CPFHub.io.

---

## Configurando as dependências

Instale as bibliotecas necessárias para o formulário:

```javascript
// Terminal
// npm install react-hook-form @hookform/resolvers zod

// lib/schemas.js
import { z } from 'zod';

function validarDigitosCpf(cpf) {
 const digitos = cpf.split('').map(Number);
 if (new Set(digitos).size === 1) return false;

 let soma = 0;
 for (let i = 0; i < 9; i++) soma += digitos[i] * (10 - i);
 const d1 = soma % 11 < 2 ? 0 : 11 - (soma % 11);

 soma = 0;
 for (let i = 0; i < 10; i++) soma += digitos[i] * (11 - i);
 const d2 = soma % 11 < 2 ? 0 : 11 - (soma % 11);

 return digitos[9] === d1 && digitos[10] === d2;
}

export const cadastroSchema = z.object({
 cpf: z.string()
 .transform((val) => val.replace(/\D/g, ''))
 .pipe(
 z.string()
 .length(11, 'CPF deve conter 11 dígitos')
 .refine(validarDigitosCpf, 'Dígitos verificadores inválidos')
 ),
 nome: z.string().min(3, 'Nome deve ter pelo menos 3 caracteres'),
 email: z.string().email('Email inválido'),
 dataNascimento: z.string().min(1, 'Data de nascimento é obrigatória'),
});
```

| Biblioteca | Papel |
|---|---|
| `react-hook-form` | Gerenciamento de estado do formulário |
| `zod` | Validação de schema com tipagem |
| `@hookform/resolvers` | Integração entre React Hook Form e Zod |

---

## Componente de input de CPF com máscara

Crie um componente de input que aplica máscara e validação visual ao CPF:

```jsx
// components/CpfInput.jsx
'use client';

import { forwardRef, useCallback } from 'react';

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

const CpfInput = forwardRef(function CpfInput(
 { onChange, onBlur, name, error, validado, ...props },
 ref
) {
 const handleChange = useCallback(
 (e) => {
 const mascarado = aplicarMascara(e.target.value);
 e.target.value = mascarado;
 onChange(e);
 },
 [onChange]
 );

 let borderClass = 'border-gray-300';
 if (error) borderClass = 'border-red-500';
 else if (validado) borderClass = 'border-green-500';

 return (
 <div>
 <label htmlFor={name} className="block text-sm font-medium mb-1">
 CPF
 </label>
 <input
 ref={ref}
 id={name}
 name={name}
 type="text"
 inputMode="numeric"
 maxLength={14}
 placeholder="000.000.000-00"
 onChange={handleChange}
 onBlur={onBlur}
 className={`w-full p-3 border rounded ${borderClass}`}
 {...props}
 />
 {error && <p className="text-red-500 text-sm mt-1">{error}</p>}
 </div>
 );
});

export default CpfInput;
```

---

## Route handler para validação

Crie o endpoint que válida o CPF e retorna dados para preenchimento automático:

```javascript
// app/api/validar-cpf/route.js

export async function POST(request) {
 const { cpf } = await request.json();
 const cpfLimpo = cpf.replace(/\D/g, '');

 if (cpfLimpo.length !== 11) {
 return Response.json(
 { valido: false, message: 'CPF incompleto' },
 { status: 400 }
 );
 }

 try {
 const response = await fetch(
 `${process.env.CPFHUB_BASE_URL}/cpf/${cpfLimpo}`,
 {
 headers: { 'x-api-key': process.env.CPFHUB_API_KEY },
 }
 );

 if (!response.ok) {
 return Response.json({
 valido: true,
 encontrado: false,
 message: 'CPF válido, mas não encontrado na base',
 });
 }

 const dados = await response.json();

 if (dados.success) {
 return Response.json({
 valido: true,
 encontrado: true,
 nome: dados.data.name,
 dataNascimento: dados.data.birthDate,
 genero: dados.data.gender,
 });
 }

 return Response.json({ valido: true, encontrado: false });
 } catch {
 return Response.json(
 { valido: false, message: 'Erro na validação' },
 { status: 502 }
 );
 }
}
```

---

## Formulário completo de cadastro

Monte o formulário com validação integrada e preenchimento automático:

```jsx
// components/FormularioCadastro.jsx
'use client';

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useState } from 'react';
import { cadastroSchema } from '@/lib/schemas';
import CpfInput from './CpfInput';

export default function FormularioCadastro() {
 const [cpfValidado, setCpfValidado] = useState(false);
 const [submitting, setSubmitting] = useState(false);

 const {
 register,
 handleSubmit,
 setValue,
 formState: { errors },
 } = useForm({
 resolver: zodResolver(cadastroSchema),
 });

 const handleCpfBlur = async (e) => {
 const cpf = e.target.value;
 if (cpf.replace(/\D/g, '').length !== 11) return;

 try {
 const response = await fetch('/api/validar-cpf', {
 method: 'POST',
 headers: { 'Content-Type': 'application/json' },
 body: JSON.stringify({ cpf }),
 });

 const dados = await response.json();

 if (dados.encontrado) {
 setValue('nome', dados.nome);
 setValue('dataNascimento', dados.dataNascimento);
 setCpfValidado(true);
 }
 } catch {
 console.error('Erro ao validar CPF');
 }
 };

 const onSubmit = async (data) => {
 setSubmitting(true);
 try {
 console.log('Dados do cadastro:', data);
 } finally {
 setSubmitting(false);
 }
 };

 return (
 <form onSubmit={handleSubmit(onSubmit)} className="space-y-4 max-w-lg mx-auto">
 <CpfInput
 {...register('cpf')}
 error={errors.cpf?.message}
 validado={cpfValidado}
 onBlur={handleCpfBlur}
 />

 <div>
 <label htmlFor="nome" className="block text-sm font-medium mb-1">Nome</label>
 <input id="nome" {...register('nome')} className="w-full p-3 border rounded" />
 {errors.nome && <p className="text-red-500 text-sm mt-1">{errors.nome.message}</p>}
 </div>

 <div>
 <label htmlFor="email" className="block text-sm font-medium mb-1">Email</label>
 <input id="email" type="email" {...register('email')} className="w-full p-3 border rounded" />
 {errors.email && <p className="text-red-500 text-sm mt-1">{errors.email.message}</p>}
 </div>

 <div>
 <label htmlFor="dataNascimento" className="block text-sm font-medium mb-1">
 Data de Nascimento
 </label>
 <input id="dataNascimento" type="date" {...register('dataNascimento')}
 className="w-full p-3 border rounded" />
 {errors.dataNascimento && (
 <p className="text-red-500 text-sm mt-1">{errors.dataNascimento.message}</p>
 )}
 </div>

 <button type="submit" disabled={submitting}
 className="w-full p-3 bg-blue-600 text-white rounded">
 {submitting ? 'Cadastrando...' : 'Cadastrar'}
 </button>
 </form>
 );
}
```

---

## Perguntas frequentes

### Por que usar um Route Handler do Next.js em vez de chamar a API de CPF diretamente do cliente?

Chamar a API da CPFHub.io diretamente do navegador exporia sua API key para qualquer usuário que inspecionar as requisições de rede. O Route Handler (`app/api/validar-cpf/route.js`) roda no servidor, mantém a chave segura em variáveis de ambiente e ainda permite adicionar validação, rate limiting e logging antes de repassar a consulta. A [documentação do Next.js](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) detalha como criar e proteger esses endpoints.

### Como funciona o preenchimento automático de nome e data de nascimento?

Quando o usuário termina de digitar o CPF (evento `onBlur`), o componente chama o Route Handler, que consulta a CPFHub.io. Se o CPF for encontrado, a API retorna `name` e `birthDate`, e o formulário usa `setValue` do React Hook Form para preencher os campos automaticamente. O usuário vê o feedback imediato sem precisar digitar as informações manualmente.

### A API da CPFHub.io pode retornar erro 429 e travar o formulário?

Não. A CPFHub.io nunca bloqueia requisições nem retorna HTTP 429. Ao superar as 50 consultas mensais do plano gratuito, cada consulta adicional é cobrada automaticamente a R$0,15. Para formulários de cadastro com alto volume, isso significa que o fluxo nunca é interrompido por limite de requisições.

### Como validar o CPF sintaticamente no cliente antes de chamar a API?

O schema Zod deste artigo já inclui a função `validarDigitosCpf`, que verifica os dígitos verificadores localmente. Essa validação roda no navegador antes de qualquer chamada de rede, evitando consultas desnecessárias à API para CPFs obviamente inválidos (como `111.111.111-11`) e melhorando a performance percebida pelo usuário.

### Leia também

- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)
- [10 erros mais comuns ao integrar uma API de CPF e como evitá-los](https://cpfhub.io/blog/10-erros-mais-comuns-ao-integrar-uma-api-de-cpf)

---

## Conclusão

Um formulário de cadastro com validação de CPF integrada eleva a qualidade dos dados e a experiência do usuário. A combinação de React Hook Form para gerenciamento de estado, Zod para validação de schema e a API do CPFHub para enriquecimento de dados cria uma solução completa e profissional.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e adicione validação e preenchimento automático de CPF ao seu formulário Next.js ainda hoje.

