Learn and Teach Coding

React Login Form Validation

Build a login form with validation using React Hook Form and Zod.

ReactBeginnerformsvalidationreact-hook-formzod

Live preview

Code walkthrough

React Hook Form keeps form state out of React's render cycle, so even large forms stay fast. Pairing it with Zod gives you a single schema that drives both runtime validation and TypeScript types. Use View full code above for the whole component.

1. One schema, two jobs

The Zod schema validates at runtime and generates the form's TypeScript type via z.infer, so the rules and the types can never disagree.

const schema = z.object({
  email: z.string().email("Enter a valid email"),
  password: z.string().min(8, "At least 8 characters"),
});

type FormValues = z.infer<typeof schema>;

2. Wiring the form

useForm with zodResolver(schema) connects Zod to React Hook Form. We pull out register (to bind inputs), handleSubmit (validates before calling us), and formState (errors + submitting flag).

const {
  register,
  handleSubmit,
  formState: { errors, isSubmitting },
} = useForm<FormValues>({ resolver: zodResolver(schema) });

3. Registering inputs and showing errors

{...register("email")} wires an input to the form with no useState. When validation fails, the matching errors.* entry holds the message — render it with role="alert" so screen readers announce it.

<input id="email" type="email" {...register("email")} />
{errors.email && <p role="alert">{errors.email.message}</p>}

noValidate on the <form> turns off the browser's native bubbles so Zod is the single source of truth for validation messages.

4. Submitting

handleSubmit(onSubmit) only calls onSubmit once the data is valid, and isSubmitting lets you disable the button while the request is in flight.

async function onSubmit(values: FormValues) {
  await fetch("/api/login", { method: "POST", body: JSON.stringify(values) });
}

Related examples

Beginner

Login Form UI

A clean, accessible login form component for React with email, password, and social sign-in.

View example
Beginner

Form Validation — Simple Input Validation

Validate a sign-in form by hand with useState — inline errors, validate-on-blur, and a submit guard. No libraries.

View example
Intermediate

Form Validation — using Formik

Validate a sign-in form with Formik and Yup — declarative fields, a validation schema, and built-in touched/error state.

View example