React hook form basics

React Hook Form uses uncontrolled inputs for performance. You call a register function that returns props and spread them onto the input. The library tracks the value internally. handleSubmit wraps your submit handler and gives you the form data when it fires.

LoginForm.tsx
tsx
'use client';

import { useForm } from 'react-hook-form';

interface FormValues {
  username: string;
  password: string;
}

export function LoginForm() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<FormValues>();

  async function onSubmit(data: FormValues) {
    await login(data.username, data.password);
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('username', { required: true })} />
      {errors.username && <p>Username is required</p>}

      <input type="password" {...register('password', { required: true })} />
      {errors.password && <p>Password is required</p>}

      <button disabled={isSubmitting}>Log in</button>
    </form>
  );
}

A minimal form, values tracked by register.

Read it top to bottom. useForm returns a register function, a handleSubmit wrapper, and a formState object with errors and submission state. Each input spreads register(name) to wire itself into the library. The submit handler only runs if validation passes. No manual state, no boilerplate, just the shape of the form.

Quiz: Quiz

Loading practice…