Search across all documentation pages
In React 19, you don't need to write fully controlled inputs manually for most native fields. But controlled patterns (especially via Controller) are still recommended when working with modern UI libraries or rich interactive forms.
Controlled — React state is the single source of truth. Every keystroke updates state and re-renders.
function ControlledInput() {
const [email, setEmail] = useState("");
return (
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
);
}Uncontrolled — The DOM holds the value. You read it on submit (via ref or FormData).
function UncontrolledInput() {
const ref = useRef<HTMLInputElement>(null);
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
console.log(ref.current?.value);
}
return
The modern default for simple forms — no state, no refs, no re-renders.
async function subscribe(formData: FormData) {
"use server";
const email = formData.get("email");
await saveSubscriber(email);
}
export function NewsletterForm() {
return (
<
register() wires inputs as uncontrolled — fast, minimal re-renders.
import { useForm } from "react-hook-form";
type FormValues = { email: string; password: string };
export function LoginForm() {
const { register, handleSubmit } = useForm<
Use Controller to bridge to controlled components like shadcn/ui Select or MUI.
import { Controller, useForm } from "react-hook-form";
import { Select, SelectTrigger, SelectContent, SelectItem } from "@/components/ui/select";
export function PlanForm() {
const { control, handleSubmit } = useForm({ defaultValues: { plan: "free" } });
return (
<
<input>, <textarea>, <select> → uncontrolled with register() or server actions<Controller>value + onChange to a field that uses register() — it breaks the uncontrolled pattern.defaultValue (not value) for uncontrolled inputs to avoid the controlled/uncontrolled warning.<form action={...}>useActionState and useFormStatususeActionState and useFormStatusreset() or native requestFormResetStart with uncontrolled inputs + React 19 native hooks for any new simple or medium-sized form. Switch to react-hook-form the moment you need real-time validation, conditional fields, or complex client-side logic.
Even when using react-hook-form, you still benefit from uncontrolled input performance under the hood.