
How to Work with React Forms So They Don't Break Your Brain
How to Work with React Forms So They Don't Break Your Brain êŽë š
If youâve ever built a form in React and felt like the input fields had a mind of their own, youâre not alone. One minute your form is working fine, the next youâre staring at a blank input that wonât update. Or React throws a warning like âA component is changing an uncontrolled input of type text to be controlled.â
and youâre not even sure what that means.
I didnât really get it either until I realized that React doesnât just read form inputs â it can own
them. And whether you let React control your inputs or let the DOM
handle them makes a real difference in how your form behaves.
In this article, Iâll break down:
- What controlled and uncontrolled components are
- Why the difference matters
- When to use each one
- And how to avoid common beginner mistakes
Youâll get real code, clear examples, and a no-nonsense guide to making React forms behave exactly how you want.
What Is a Controlled Component?
A controlled component
in React is an input (like a text box or dropdown) where React keeps track of the value.
Instead of the browser handling the input on its own, you use React state to tell the input what to show, and update that state when the user types. Basically, for every keystroke the state updates and the component re-renders.
Hereâs a basic example:
import { useState } from "react";
function NameForm() {
const [name, setName] = useState("");
return (
<input
type="text"
value={name} //Whatever the state is, that is what the value of the input field will be
onChange={(e) => setName(e.target.value)} //When you type, this function runs and updates the state
/>
);
}
React re-renders with the new value, keeping the UI and the data in sync. You're always in control of whatâs in that field.
Why use this approach?
- You always know the current value.
- Itâs easy to validate, reset, or change the input from code.
- Itâs the standard approach in most React apps.
What Is an Uncontrolled Component?
An uncontrolled component
is the opposite of what we just looked at. Instead of using React state to manage the input, you let the browser handle it on its own, like a regular HTML form.
To get the value, you use something called a ref
(short for âreferenceâ) to reach into the DOM and grab it when you need it.
In React, refs
are created using a built-in hook called useRef
. This hook lets you create a reference to a DOM element (like an <input>
), so you can directly access its current value whenever you need it (for example, when a form is submitted).
Unlike useState
, which tracks changes and causes re-renders, useRef
simply gives you a way to "point to" or "reach into" an element in the DOM without triggering re-renders. Itâs useful when you donât want React to manage the inputâs state, but you still need to read its value later.
Hereâs what that looks like:
import { useRef } from "react";
function NameForm() {
const inputRef = useRef();
const handleSubmit = () => {
alert(inputRef.current.value); //displays the value of the input element
};
return (
<>
<input type="text" ref={inputRef} />; //gives you direct access to the input element
<button onClick={handleSubmit}>Submit</button> //
</>
);
}
React isnât involved in tracking every keystroke. It only checks the value when you ask for it and the input keeps track of itâs value on itâs own.
Why use this?
- Itâs simpler for quick forms where you only need the value at the end (like on submit).
- It avoids re-renders while typing, which can be useful in performance-sensitive apps.
But: itâs harder to do things like validation, real-time updates, or syncing with other parts of your app.
Controlled vs Uncontrolled: What's the Difference?
Now that youâve seen both, letâs make the differences crystal clear.
Controlled Components | Uncontrolled Components |
---|---|
React is in charge. | The browser is in charge. |
You use useState to store the value. | You use useRef to access the value. |
You update the value with onChange. | The input keeps its own value. |
React re-renders the input every time the value changes. | You access it using a ref only when you need it. |
Think of a controlled component like a parent carefully tracking what their kid is writing in a notebook, checking every word as it's written.
An uncontrolled component is more like letting the kid write freely and just reading what they wrote at the end.
When to Use Controlled vs Uncontrolled Components
Both controlled and uncontrolled components have their place. The key is knowing when each one makes sense for your project and what you want to achieve.
Use Controlled Components When:
- You need to validate input while the user types.
Example: Show an error if the user leaves a field empty. - You want to enable/disable buttons based on input.
Example: Disable the âSubmitâ button until all fields are filled. - Youâre building dynamic forms.
Example: Show or hide fields based on what the user selects. - You need to sync input values with other state.
Example: Update a live preview as the user types.
Use Uncontrolled Components When:
- You just need the value when the form is submitted.
Example: A basic contact form that sends data once. - You donât need to update the UI based on input.
- You want better performance in large forms.
Example: Dozens of inputs that donât need to trigger re-renders on every change.
In short:
- If you need to watch, validate, or react to what the user types(interact with your appâs state or UI), go with controlled.
- If you just need to grab the value later, uncontrolled can work fine.
Conclusion
Controlled vs. Uncontrolled Components might seem like a small technical distinction at first but understanding the difference gives you much more control over how your forms behave in React.
Controlled components put you in the driverâs seat. React manages the form state for you, so you always know whatâs in your inputs. This makes it easy to validate user input in real-time, sync form data with other parts of your app, and build more interactive, dynamic experiences.
Uncontrolled components, on the other hand, keep things minimal. The browser handles the inputâs state, and you only reach in to get the value when you need it, usually using a ref.
Thereâs no one-size-fits-all answer for which is better. It depends entirely on your needs. If your form needs to react to user input as it changes or connect tightly with app logic, go controlled. If you just need to collect some values and move on, uncontrolled might be simpler.