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.

Redação CPFHub.io
Redação CPFHub.io
··7 min de leitura
Como criar um formulário de cadastro com validação de CPF em Next.js

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:

// 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'),
});
BibliotecaPapel
react-hook-formGerenciamento de estado do formulário
zodValidação de schema com tipagem
@hookform/resolversIntegraçã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:

// 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:

// 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:

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


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

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