Contexto
Os dois resolvem o mesmo problema (gerenciar estado, validação e submissão de formulários em React), mas partem de decisões arquiteturais opostas sobre onde o estado do formulário mora. Formik foi por anos a escolha padrão: cada campo é um componente controlado, o valor vive no estado do React e o formulário re-renderiza a cada tecla digitada. React Hook Form fez a aposta inversa: os inputs ficam não controlados, registrados via refs com register, e a biblioteca só re-renderiza o que realmente precisa. Essa diferença de modelo (controlado vs não controlado) é a raiz de quase tudo que separa os dois na prática, de performance a boilerplate.
Há também um dado que não cabe na tabela de recursos e que pesa muito na decisão hoje: a distância de adoção e manutenção. React Hook Form passa de 58 milhões de downloads semanais e segue em desenvolvimento muito ativo; Formik está na casa de 4.3 milhões, parado na v2.4.9, com o ritmo de manutenção bem mais devagar. Não é um empate técnico com desempate por gosto: React Hook Form virou o padrão moderno do ecossistema.
Quando escolher React Hook Form
React Hook Form compensa em praticamente qualquer projeto novo, e o ganho aparece mais forte em formulários grandes, onde o modelo não controlado evita re-renderizar o formulário inteiro a cada tecla:
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const schema = z.object({ email: z.string().email() });
type FormData = z.infer<typeof schema>;
export function SignupForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({ resolver: zodResolver(schema) });
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<input {...register("email")} />
{errors.email && <span>{errors.email.message}</span>}
<button type="submit">Enviar</button>
</form>
);
}
O register conecta o input por ref, sem transformá-lo em componente controlado, e o handleSubmit só dispara depois da validação passar. A validação sai barata via resolver: o mesmo schema Zod (ou Yup) que você já usa no backend valida o formulário, sem reescrever regras. O boilerplate é mínimo (register e handleSubmit cobrem o caso comum) e o bundle é minúsculo, sem dependências extras. O único ponto de atenção é integrar componentes de UI controlados de terceiros (um Select de biblioteca, por exemplo): aí entra o <Controller>, que faz a ponte entre o modelo não controlado do React Hook Form e um componente que espera value/onChange.
Quando escolher Formik
Formik faz sentido, sobretudo, quando o custo é de manutenção e não de escolha: formulários legados já escritos nele, que funcionam e passam nos testes, raramente justificam a reescrita só pela migração.
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
const schema = Yup.object({ email: Yup.string().email().required() });
export function SignupForm() {
return (
<Formik
initialValues={{ email: "" }}
validationSchema={schema}
onSubmit={(values) => console.log(values)}
>
<Form>
<Field name="email" />
<ErrorMessage name="email" component="span" />
<button type="submit">Enviar</button>
</Form>
</Formik>
);
}
O modelo controlado tem uma vantagem legítima: como cada Field já é controlado, componentes de UI controlados de terceiros encaixam com menos cerimônia, sem precisar de um wrapper como o Controller. Em troca, você paga em re-renders (o formulário re-renderiza a cada digitação por padrão) e em verbosidade (Formik, Form, Field e ErrorMessage para o que o React Hook Form resolve com duas funções). O ponto que mais pesa não está no código: Formik desacelerou. Começar um projeto novo nele hoje é escolher a peça com menos manutenção ativa e uma comunidade que já migrou de lado.
Veredito
Para projeto novo, a resposta é direta: React Hook Form. O modelo não controlado entrega re-renders mínimos e bundle pequeno, o boilerplate é menor, a validação por resolver reaproveita o schema Zod ou Yup que você já tem, e a adoção massiva significa exemplos, integrações e respostas para praticamente qualquer caso. A única fricção real, integrar componentes controlados de terceiros, é resolvida com o Controller, um padrão bem documentado.
Formik não é uma escolha errada, é uma escolha datada. Ele foi o padrão do ecossistema por bons motivos e continua funcionando bem no código que já existe, então migrar um formulário Formik maduro e testado só pela migração raramente compensa. Mas a distância de manutenção e de adoção (cerca de 58M contra 4.3M de downloads semanais, com Formik parado na v2.4.9) tornou a decisão para greenfield quase automática: quem escreveria "vou usar Formik" num projeto novo deveria primeiro se perguntar por que não React Hook Form.