diogodev_
Conteúdo

Criando formulários com React Hook Form e Zod

Criar formulários é uma das tarefas mais comuns em aplicações web. Porém, validar campos, exibir mensagens de erro e garantir tipagem consistente pode rapidamente se tornar trabalhoso. A combinação entre React Hook Form e Zod resolve esse problema de forma simples, performática e com excelente integração com TypeScript.

O que é React Hook Form?

React Hook Form é uma biblioteca para gerenciamento de formulários focada em performance e simplicidade.

Ela oferece:

  • Validação eficiente;
  • Menor quantidade de re-renderizações;
  • Integração com TypeScript;
  • Facilidade para trabalhar com formulários complexos.

O que é Zod?

Zod é uma biblioteca de validação baseada em schemas.

Com ela podemos definir regras de negócio e ainda obter tipagem automática no TypeScript.

Exemplo:

import { z } from "zod";

const userSchema = z.object({
name: z.string(),
email: z.email(),
age: z.number()
});

Dessa forma, a validação e os tipos ficam centralizados em um único lugar.

Instalando as dependências

npm install react-hook-form zod @hookform/resolvers

Criando o schema

Vamos começar definindo as regras do formulário.

import { z } from "zod";

export const registerSchema = z.object({
name: z
.string()
.min(3, "Nome deve possuir pelo menos 3 caracteres"),

email: z
.email("E-mail inválido"),

password: z
.string()
.min(6, "Senha deve possuir no mínimo 6 caracteres")
});

Agora podemos obter os tipos automaticamente:

export type RegisterFormData =
z.infer<typeof registerSchema>;

Configurando o React Hook Form

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const {
register,
handleSubmit,
formState: { errors }
} = useForm<RegisterFormData>({
resolver: zodResolver(registerSchema)
});

Com isso, todas as validações serão feitas pelo Zod.

Criando o formulário

<form onSubmit={handleSubmit(onSubmit)}>

<input
{...register("name")}
placeholder="Nome"
/>

{errors.name && (
<span>{errors.name.message}</span>
)}

<input
{...register("email")}
placeholder="E-mail"
/>

{errors.email && (
<span>{errors.email.message}</span>
)}

<input
type="password"
{...register("password")}
placeholder="Senha"
/>

{errors.password && (
<span>{errors.password.message}</span>
)}

<button type="submit">
Salvar
</button>

</form>

As mensagens de erro são exibidas automaticamente.

Enviando os dados

function onSubmit(data: RegisterFormData) {

console.log(data);

}

Como o tipo é inferido pelo Zod, o objeto recebido já possui tipagem completa.

Validações mais avançadas

Campos opcionais

const schema = z.object({
phone: z.string().optional()
});

Valor mínimo

age: z.number().min(18)

E-mail

email: z.email()

URL

website: z.url()

Enum

status: z.enum([
"active",
"inactive"
])

Confirmando senha

Uma validação bastante comum:

const schema = z
.object({
password: z.string(),
confirmPassword: z.string()
})
.refine(
data => data.password === data.confirmPassword,
{
path: ["confirmPassword"],
message: "As senhas não coincidem"
}
);

Trabalhando com valores padrão

useForm({
resolver: zodResolver(schema),
defaultValues: {
name: "Diogo",
email: ""
}
});

Isso é útil em formulários de edição.

Resetando o formulário

const { reset } = useForm();

reset();

Ou:

reset({
name: "",
email: ""
});

Utilizando Controller

Quando trabalhamos com componentes externos, como Material UI ou shadcn/ui, podemos utilizar o Controller.

<Controller
control={control}
name="name"
render={({ field }) => (
<Input {...field} />
)}
/>

Estrutura recomendada

Uma organização comum:

forms

├── schemas
│ ├── userSchema.ts
│ ├── loginSchema.ts

├── components
│ ├── FormInput.tsx

└── pages

Separar schemas dos componentes facilita a manutenção.

Boas práticas

Centralize os schemas

Evite criar validações diretamente nos componentes.

Utilize z.infer

type FormData = z.infer<typeof schema>;

Isso evita duplicação de tipos.

Crie componentes reutilizáveis

Por exemplo:

  • Input;
  • Select;
  • Textarea.

Exiba mensagens amigáveis

Prefira:

"E-mail inválido"

Ao invés de mensagens genéricas.

Utilize TypeScript

A integração entre RHF e Zod é excelente e reduz muitos erros em tempo de desenvolvimento.

Vantagens

  • Excelente performance;
  • Menos re-renderizações;
  • Validação simples;
  • Integração com TypeScript;
  • Código mais limpo;
  • Fácil manutenção.

Quando utilizar?

Essa combinação é ideal para:

  • Sistemas administrativos;
  • Formulários complexos;
  • Aplicações React e Next.js;
  • Projetos com TypeScript;
  • Aplicações que exigem validação robusta.

Conclusão

React Hook Form e Zod formam uma das melhores combinações para criação de formulários em aplicações React. Com poucas linhas é possível obter validação, tipagem e uma excelente experiência para o usuário.

Se você trabalha com React e TypeScript, vale a pena considerar essa abordagem nos próximos projetos.

Saiba mais