React Forms Handling
Build robust forms with controlled inputs, uncontrolled refs, FormData extraction, and validation handling.
TL;DR
- Synchronize form inputs with component state using
controlledinput bindings. - Extract raw multi-field form submissions easily using the native
FormDataAPI. - Prevent default page reload cycles by intercepting the
onSubmitevent.
Controlled Inputs
Single Controlled InputBind input value to state and update on change.
const [email, setEmail] = useState('');
return (
<InputBase
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
);Controlled CheckboxBind boolean checked property rather than text value.
const [agree, setAgree] = useState(false);
return (
<CheckboxBase
checked={agree}
onChange={(e) => setAgree(e.target.checked)}
/>
);Controlled Select DropdownSynchronize selected option value with state string.
const [role, setRole] = useState('user');
const onChange = (e: any) => setRole(e.target.value);
return (
<SelectBase value={role} onChange={onChange}>
<option value="user">User</option>
<option value="admin">Admin</option>
</SelectBase>
);Multi-Field Form Management
Single State Object HandlerHandle multiple fields dynamically using input name attributes.
const [form, setForm] = useState({
first: '',
last: '',
});
const handleChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
const { name, value } = e.target;
setForm((prev) => ({ ...prev, [name]: value }));
};Native FormData ExtractionRead all form fields at once during submission without state.
function handleSubmit(
e: React.FormEvent<HTMLFormElement>
) {
e.preventDefault();
const data = new FormData(e.currentTarget);
const payload = Object.fromEntries(data);
console.log(payload);
}Reset Form StateClear all inputs back to their original initial values.
const handleReset = () => setForm(initialState);Form Validation Patterns
Inline Validation StateCompute validation errors during rendering based on state.
const emailValid = email.includes('@');
const isErr = email && !emailValid;
const error = isErr ? 'Invalid email' : null;Submit Validation GuardValidate all fields before executing network mutation.
function onSubmit(e: React.FormEvent) {
e.preventDefault();
if (!form.email || !form.password) {
setError('All fields required');
return;
}
proceed();
}Submit Button DisablingDisable submit button until form requirements are met.
const isDisabled = !isValid || isSubmitting;
return (
<ButtonBase type="submit" disabled={isDisabled}>
Save Changes
</ButtonBase>
);Uncontrolled Forms with Refs
Direct Ref AccessRead input value only when submit button is pressed.
const fileInputRef = useRef<HTMLInputElement>(null);
const onUpload = () => {
const files = fileInputRef.current?.files;
};Default Value UncontrolledPre-populate uncontrolled input using defaultValue prop.
<InputBase
defaultValue="Initial Name"
name="username"
/>Form Reset NativeReset all input DOM elements using HTMLFormElement.reset.
formRef.current?.reset();Tips
- Use controlled inputs when you need instant real-time validation or conditional UI disabling as the user interacts with
inputs. - Leverage native HTML attributes like
required,min, andpatternalongside React handlers for accessible validation.
Warnings
- Switching an input from uncontrolled to controlled by passing
undefinedinitial state causes React console errors. - Avoid binding every keystroke to state if the form only needs data upon final submission; use
FormDatainstead.
In Practice
Build an accessible user registration form with validation feedback and FormData extraction.
- Intercept default form submission using preventDefault.
- Extract field values using the native browser FormData API.
- Validate required fields and display helpful inline error messages.
- Submit validated payload to remote endpoint asynchronously.
export function RegisterForm({ onSave }: FormProps) {
const [err, setErr] = useState<string | null>(null);
function onSubmit(e: React.FormEvent) {
e.preventDefault();
const data = new FormData(e.target as any);
const email = data.get('email') as string;
if (!email || !email.includes('@')) {
setErr('Valid email required');
return;
}
setErr(null);
onSave({ email });
}
return (
<Form onSubmit={onSubmit}>
{err && <Alert text={err} />}
<InputBase name="email" placeholder="Email" />
<ButtonBase type="submit">Save</ButtonBase>
</Form>
);
}FAQ
Controlled inputs bind their value prop directly to React state and update via onChange. Uncontrolled inputs store their value in the DOM and are read on demand using refs or FormData.
This occurs when an input initial value is undefined or null, then later receives a string. Always initialize controlled input state with an empty string '' to maintain controlled status.
React 19 introduces native Form Actions, allowing form submission functions to pass directly into the form action attribute, managing pending states automatically.