Forms

React Hook Form + Zod / TanStack Form

Our pick

React Hook Form + Zod

Lots of people use it, so answers are easy to find when you search. shadcn/ui's forms use this combo too

Compare the result

We built the same thing with each library. Try them out.

React Hook Form + Zod
TanStack Form

Compare the code

Edit the code and the preview on the right updates.

To run this demo in your own project:npx shadcn@latest add button input label

import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"
import { Button } from "./components/ui/button"
import { Input } from "./components/ui/input"
import { Label } from "./components/ui/label"

// ① Rules: write all the validation rules in one place
const schema = z.object({
  email: z.email("Enter a valid email address"),
  password: z.string().min(8, "Use at least 8 characters").regex(/[0-9]/, "Include at least one number"),
})

// Build the type of the form values from the rules
type FormValues = z.infer<typeof schema>

export default function App() {
  // Set up the form (the rules are passed in through zodResolver)
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FormValues>({ resolver: zodResolver(schema) })

  // Called only when every rule passes
  const onSubmit = (values: FormValues) => {
    alert("Logged in: " + values.email)
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-4 p-5">
      <div className="flex flex-col gap-2">
        <Label htmlFor="email">Email</Label>
        {/* ② Connect: hook the input up to the form */}
        <Input id="email" {...register("email")} />
        {/* ③ Report: show the error when a rule fails */}
        {errors.email && (
          <p className="text-sm text-destructive">{errors.email.message}</p>
        )}
      </div>

      <div className="flex flex-col gap-2">
        <Label htmlFor="password">Password</Label>
        <Input id="password" type="password" {...register("password")} />
        {errors.password && (
          <p className="text-sm text-destructive">{errors.password.message}</p>
        )}
      </div>

      <Button type="submit">Log in</Button>
    </form>
  )
}

Comparison

As of October 2026

React Hook Form + Zod

Our pickOfficial site ↗

Features

The most popular combo. Just add register to an input to collect its value, and write all your validation rules together with Zod

Best for

The safe default. shadcn/ui's forms are built with this combo too

TanStack Form

Official site ↗

Features

A newer library from the same family as TanStack Query. You wrap each input in form.Field. Its type (TypeScript) checking is very detailed

Best for

You want it to match other TanStack libraries. You have many inputs and want types to catch mistakes