React Hook Form, Zod and Shadcn

mar 7 ott 2025

React Hook Form, Zod and Shadcn

React Hook Form, Zod & shadcn: The Holy Trinity of Modern Forms

Let’s be honest — building forms in React used to be painful.
Managing state manually, writing endless onChange handlers, and validating input like a caveman with if statements... not exactly fun times.

But then came React Hook Form, Zod, and shadcn/ui, and suddenly —
forms became elegant, type-safe, and actually enjoyable to work with!

So let’s talk about why these three tools work so well together.


React Hook Form: The Brain

If your form were a person, React Hook Form (RHF) would be the brain — smart, efficient, and surprisingly chill under pressure.

RHF focuses on:

  • Keeping re-renders to a minimum
  • Giving you clean APIs like useForm() and Controller
  • Integrating easily with UI libraries

The best part? It doesn’t try to control everything. Instead, it gives you just enough structure to stay organized without forcing you into a rigid pattern.

const { register, handleSubmit } = useForm<FormData>();

<form onSubmit={handleSubmit(onSubmit)}>
  <input {...register("email")} placeholder="Email" />
  <button type="submit">Submit</button>
</form>;

Boom. A working form with validation hooks built right in.

Zod: The Validation

If RHF is the brain, Zod is the logic — the all-knowing validator and type guardian.

Zod helps you define and validate data with TypeScript superpowers. Instead of writing validation rules in multiple places, you define your schema once, and Zod takes care of:

  • Runtime validation
  • Static type inference
  • Cleaner, DRY-er code
const formSchema = z.object({
  email: z.string().email(),
  age: z.number().min(18, "You must be an adult!"),
});

Then plug it into RHF like magic:

const form = useForm<z.infer<typeof formSchema>>({
  resolver: zodResolver(formSchema),
});

Now your form is type-safe, validated, and future-proof.

Zod: The Validation

Every great duo needs a third wheel that makes them look good. Enter shadcn/ui — beautiful, accessible, and fully customizable components built on top of Radix UI and Tailwind.

Why it fits perfectly:

  • Components are unstyled enough to theme easily
  • Integration with react-hook-form is seamless
  • It gives your form a modern, consistent look out of the box

Example:

<Form {...form}>
  <form onSubmit={form.handleSubmit(onSubmit)}>
    <FormField
      control={form.control}
      name="email"
      render={({ field }) => (
        <FormItem>
          <FormLabel>Email</FormLabel>
          <FormControl>
            <Input placeholder="you@example.com" {...field} />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
    <Button type="submit">Sign up</Button>
  </form>
</Form>

It’s like React Hook Form handles the logic, Zod guards your types, and shadcn makes everything look ridiculously good.

Put them together and you get:

  • Blazing-fast forms
  • Zero duplicate validation logic
  • End-to-end type safety
  • A polished UI that actually looks good

If you’re building a form in 2025 and you’re not using this combo… You might be doing it wrong. 😬