█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/Programming Languages/React (Standalone Deep Dive)/Forms in React
45 minIntermediate

Forms in React

After this lesson, you will be able to: Use controlled vs uncontrolled inputs; react-hook-form + Zod for production forms.

Forms are 80% of frontend bugs. The right library saves real hours.

Prerequisites:React Patterns

Controlled vs uncontrolled

Trade-offs.

html
// Controlled: state is in React; input renders FROM state
const [name, setName] = useState("");
<input value={name} onChange={(e) => setName(e.target.value)} />
// Uncontrolled: state in the DOM; React reads via ref
const inputRef = useRef<HTMLInputElement>(null);
<input ref={inputRef} defaultValue="" />
// Read on submit: inputRef.current.value
// Controlled wins for: realtime validation, formatting, conditional rendering.
// Uncontrolled wins for: performance on big forms (no re-render per keystroke).

react-hook-form (the standard)

Uncontrolled under the hood; ergonomic API.

html
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const Schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
type FormData = z.infer<typeof Schema>;
function LoginForm() {
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>({
resolver: zodResolver(Schema),
});
return (
<form onSubmit={handleSubmit(async (data) => await login(data))}>
<input {...register("email")} />
{errors.email && <p>{errors.email.message}</p>}
<input type="password" {...register("password")} />
{errors.password && <p>{errors.password.message}</p>}
<button disabled={isSubmitting}>Sign in</button>
</form>
);
}

Accessibility checklist

Every input has a label (`<label htmlFor>` or wrapping). Error messages linked via aria-describedby. Submit button accessible (real `<button type="submit">`). Don't disable submit only on validation error, set aria-invalid + show error.

Common mistakes only experienced React devs avoid

Hand-rolling form state with 10 useStates. RHF takes 10 minutes to learn; saves days. Forgetting to disable submit during in-flight requests. Users double-click. Validating only on submit. Real-time validation (after blur) gives better UX. No labels. Accessibility AND form autofill break.

Quick Check

Why pair Zod with react-hook-form?

Pick the senior answer.

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←React Patterns
Back to React (Standalone Deep Dive)
Testing React→