Form approaches in React: controlled, uncontrolled, Actions and React Hook Form
Choose how to build forms: controlled vs uncontrolled inputs, native forms with React 19 Actions, React Hook Form, one schema (Zod) for client and server validation, field arrays, and dirty and touched state.
Comes up in: React, Machine coding
Learn#
Pooja’s 40-field insurance form lags on a mid-range phone: every keystroke re-renders the whole form. And her server accepts emails the client rejected, because the rules were written twice and drifted.
A controlled input stores its value in React state and updates on every keystroke; easy to read and change, but it re-renders. An uncontrolled input keeps its value in the DOM, and you read it on submit, which is fast for big forms. React 19 Actions (RCT.05.01) and React Hook Form both build on uncontrolled inputs.
Schema validation writes the rules once and uses them in both places, the browser and the server:
import { z } from "zod";
// One schema, used by the form in the browser AND by the Server Action: rules are written once.
const Signup = z.object({
name: z.string().trim().min(2, "Enter your name"),
email: z.string().email("Enter an email like name@example.com"),
pin: z.string().regex(/^\d{6}$/, "Enter the 6-digit PIN code"),
});
const result = Signup.safeParse({ name: "A", email: "asha@", pin: "4110" });
if (!result.success) console.log(z.flattenError(result.error).fieldErrors);
console.log(Signup.parse({ name: " Asha ", email: "asha@example.in", pin: "411001" }));Output (verified)
{
name: [ 'Enter your name' ],
email: [ 'Enter an email like name@example.com' ],
pin: [ 'Enter the 6-digit PIN code' ]
}
{ name: 'Asha', email: 'asha@example.in', pin: '411001' }State
- keystroke → render Form (40 fields)
Page
empty
Controlled form: every keystroke calls setState, so the whole 40-field form re-renders on each key.
Read the steps as text
- Controlled form: every keystroke calls setState, so the whole 40-field form re-renders on each key.
- Uncontrolled form (native or React Hook Form): the DOM keeps the value; React reads it on submit or blur.
- On submit, one schema checks the values in the browser and shows field errors at once.
- The same schema runs again in the Server Action, because the browser check can be skipped.
Predict#
Predict first
Client-side validation passes. Can you skip validating the same data in the Server Action?
Anyone can call the server directly (RCT.06.04). Client validation is for user experience; server validation is for safety. A shared schema makes doing both free.
Interview angle#
Say it like this — about 30 seconds
“Controlled inputs keep values in React state: simple, and good for instant formatting or dependent fields, but each keystroke re-renders. Uncontrolled inputs keep values in the DOM, which scales to large forms; React Hook Form and React 19 form Actions build on that. I define validation once with a schema like Zod, run it in the browser for quick feedback and again on the server for safety, and infer the TypeScript type from it. Errors are tied to fields with aria-describedby and aria-invalid.”
Follow-up Controlled vs uncontrolled forms: performance trade-offs?
Follow-up How do you validate forms on client and server without duplicating rules?
Follow-up How do you make form errors accessible?
Practice#
Guided
Build the form state a form library keeps: values, dirty and touched fields, and a field array.
Done when
- Dirty fields compare against the initial values, including arrays.
- Touched fields are recorded once, in order.
- Appending and removing items never mutates the old array, and reset restores everything.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
Controlled vs uncontrolled input?
Controlled: value in React state. Uncontrolled: value in the DOM, read when needed.
How do you avoid writing validation rules twice?
One schema shared by the client form and the server handler.
What does dirty mean for a field?
Its value differs from the initial value.
Why validate on the server even after client validation?
Requests can bypass the form.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Use controlled inputs when the UI reacts to each keystroke: formatting a card number, enabling fields based on others, live search. Otherwise uncontrolled scales better.
Check yourself: When is a controlled input the right choice?
When the UI must react to each keystroke, such as formatting or live dependent fields.
How it works underneathSenior rounds
form action plus useActionState handles many forms with no library.Check yourself: How does React Hook Form avoid re-renders?
It registers inputs with refs and reads values from the DOM, re-rendering only what subscribes.
Edge cases and trapsWhere people slip
Check yourself: What is a field array?
A list of repeated field groups, such as several addresses, that users can add and remove.
In productionStaff-level answers
Form state belongs to the form, not global stores. Show errors after blur or submit, not while typing the first character Source: Zod: Formatting errors.
Check yourself: Which Zod helper groups errors by field?
z.flattenError(error).fieldErrors.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- Controlled vs uncontrolled forms: performance trade-offs?
- How do you validate forms on client and server without duplicating rules?
- How do you make form errors accessible?