clipboard-listEngineering

Forms & Validation

Controlled vs uncontrolled inputs, React Hook Form, Zod schema validation, server actions, and error UX.

1 item

Links1

01NotesNote

Controlled vs uncontrolled

  • Controlled: React state is the single source of truth (value + onChange). Predictable, easy to react to every keystroke — but re-renders on every change.
  • Uncontrolled: the DOM holds the value; you read it on submit via a ref. Less re-rendering, less code for simple forms. React Hook Form leans on this, which is why it's fast.

For anything beyond a trivial form, reach for React Hook Form — it minimizes re-renders, handles touched/dirty/error state, and integrates cleanly with schema validation.

Validate with a schema, share it everywhere

  • Define the shape once with Zod, then:
  • infer the TypeScript type from it (z.infer) — no duplicate type definitions,
  • validate on the client for instant feedback (via the RHF resolver),
  • validate the same schema on the server (API route / server action).
  • One schema = one source of truth for types and rules. This is the single biggest win in form code.

Client validation is UX; server validation is security

  • Client-side validation is for fast, friendly feedback. It can always be bypassed.
  • Always re-validate on the server. The server is the only trust boundary. A server action or API handler must validate its input regardless of what the client did.

Server Actions for mutations

  • In Next.js, a "use server" action can receive the form data, validate with Zod, perform the mutation, and revalidate the cache — no separate API route. Return field-level errors to drive the UI.
  • useFormStatus / useActionState give pending and error state for a clean submit experience.

Error & submission UX

  • Show errors inline, next to the field, on blur or submit — not a wall of errors at the top.
  • Disable the submit button while pending and show a spinner; prevent double-submits.
  • Preserve user input on a failed submit — never make them retype everything.
  • Associate errors with inputs via aria-describedby and mark invalid fields aria-invalid (ties into the accessibility note).