To read an uncontrolled form in React, let the DOM hold the field values and collect them with the FormData constructor when the form submits. This reads every field at once without tracking per-field state on every keystroke, and it works the same in every React version.
Leave the fields uncontrolled
An uncontrolled field has no value or checked prop. React renders it once, and the browser stores what the user types. Use defaultValue or defaultChecked only when a field needs a starting value, because those props set the initial value without controlling later edits.
<form onSubmit={handleSubmit}>
<label>
Name
<input name="fullName" defaultValue="Taylor" />
</label>
<label>
Email
<input name="email" type="email" />
</label>
<button type="submit">Send</button>
</form>The two inputs keep their own values in the DOM. Nothing re-renders as the user types, which is the main difference from a controlled form. The name attribute is what links each field to the data you read later, so it is required on every field you want to collect.
Read values on submit
The FormData constructor reads every named field from the form element. Call it inside the submit handler, after stopping the page reload, and pass the form element as the argument.
function handleSubmit(e) {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData.entries());
console.log(data);
}Submitting the form logs an object such as { fullName: "Taylor", email: "..." }. Each name attribute became a key, which is why a field without a name never appears in the result.
Object.fromEntries converts the FormData entries into a plain object, but you can also pass the FormData straight to fetch as the request body. When several checkboxes share a name, each checked box contributes its value to that key.
Read one value with get
When the handler only needs a few fields, call get on the FormData object instead of converting everything. Pass the field's name to read a single value.
const email = formData.get("email");This returns the value for the email field as a string, or null when the field is missing. Use getAll for fields that can repeat, such as several checkboxes that share a name, and use get with a file input when the user uploaded a file. A checkbox appears in FormData only when it is checked, so an unchecked box is simply absent from the result.
In React 19 you can pass a function to the form's action prop, and React hands it the FormData directly. That path runs in a transition and does not require e.preventDefault, but the onSubmit plus FormData pattern works in every React version.
When uncontrolled is the right choice
An uncontrolled form is a good fit when you only need values at submit time, or when a form has many fields and per-keystroke updates would be noisy. A controlled form with state for every input works too, but it re-renders on every keystroke and adds boilerplate for values nobody reads until submit.
A controlled form is better when the UI must react to each edit, such as live validation or an enabled submit button that depends on other fields. The onChange guide shows that controlled pattern, and the two approaches can even be mixed within one form.
Leave most fields uncontrolled and control only the one that drives live UI. A search form, for example, can keep the query input controlled for instant results while the rest of the fields stay uncontrolled and are read once on submit.
The choice is per field, not per form, so you never have to commit the whole form to one style. Start with uncontrolled fields and add state only where the UI genuinely needs it.
What to learn next
Both approaches share the same submit event, so preventing the default page reload is the next building block. When forms grow to include validation and error messages, the forms overview steps through the full pattern.
Rune AI
Key Insights
- Uncontrolled fields keep their values in the DOM.
- Give every field a name attribute.
- Read all values at once with new FormData(e.target).
- Use formData.get for one value or Object.fromEntries for all.
- Call e.preventDefault so the page does not reload.
Frequently Asked Questions
Do I need state for an uncontrolled form?
What does new FormData(e.target) return?
Does every field need a name attribute?
Conclusion
Leave fields uncontrolled, give each one a name, and read them all at once with new FormData on submit. FormData works in every React version and keeps large forms free of per-field state.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.