Must knowReact Fundamentals · 08

Inputs

value vs defaultValue (controlled vs uncontrolled), and exactly what checkboxes, radios, selects and hidden inputs put in FormData.

On this page · 9 sections
  1. In one minute
  2. The example: value vs defaultValue
  3. The example: a product review form
  4. What ends up in FormData
  5. How it works
  6. Common mistakes
  7. Interview Q&A
  8. Related
  9. Sources

In one minute

Inputs work in React as they do in HTML, with one big difference: the value prop. An input with value is controlled: React decides what it shows, so typing does nothing unless an onChange updates that value. An input with defaultValue (or defaultChecked) is uncontrolled: the prop is only the starting value, and the browser owns it afterwards. The other surprises are browser rules about what each input puts into FormData.

You’ll be able to: choose between value and defaultValue, and predict exactly what a form submits.

Inputs.tsx
1<input id="pinned" value="Ceramic Mug" />23<input id="prefilled" defaultValue="Ceramic Mug" />
What you see
Ceramic Mug
Ceramic Mug
Console: –
  1. eventThe user types in both. The browser updates each input’s text right away.
  2. render phase<input id="pinned" value="Ceramic Mug" />For the first input, React puts its value back: the value prop still says "Ceramic Mug", and nothing changed it.
  3. <input id="prefilled" defaultValue="Ceramic Mug" />The defaultValue input used the prop only once, when it mounted. The typing stays.
  4. <input id="pinned" value="Ceramic Mug" />React also warned that a value without an onChange makes a read-only field. Fix: add onChange with state, or use defaultValue.

0 / 0
value means “React decides what this shows”; defaultValue means “start here, then it’s the user’s” (recorded).

The example: value vs defaultValue

r.render(
	<>
		<input id="pinned" value="Ceramic Mug" />
		<input id="prefilled" defaultValue="Ceramic Mug" />
	</>,
)

The recorder typed XYZ into the first input and XL into the second:

{
  "pinned": "Ceramic Mug",
  "prefilled": "Ceramic Mug XL"
}

React also printed this warning, because a value without onChange can never change:

You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.

To make a controlled input editable, store the value in state and update it in onChange (the Hooks module does this). For “pre-fill but let the user edit”, use defaultValue.

The example: a product review form

function submitReview(formData: FormData) {
	log('FormData:', Object.fromEntries(formData))
}

r.render(
	<form action={submitReview}>
		<input type="hidden" name="productId" value="p1" />

		<label htmlFor="size">Size bought</label>
		<select id="size" name="size">
			<option value="">Please choose</option>
			<option value="small">Small</option>
			<option value="large">Large</option>
		</select>

		<fieldset>
			<legend>Rating</legend>
			<label><input type="radio" name="rating" value="5" /> Great</label>
			<label><input type="radio" name="rating" value="3" /> Okay</label>
		</fieldset>

		<label><input type="checkbox" name="recommend" /> I recommend it</label>

		<label htmlFor="title">Title</label>
		<input id="title" name="title" defaultValue="Love it" />

		<button type="submit">Post review</button>
	</form>,
)

Submitted without touching anything:

FormData: {"productId":"p1","size":"","title":"Love it"}

Submitted after choosing Large, “Great” and ticking “I recommend it”:

FormData: {"productId":"p1","size":"large","rating":"5","recommend":"on","title":"Love it"}

What ends up in FormData

Input Not touched Chosen / checked
<input type="hidden"> always sent (productId: "p1") —
<select> the first option’s value (size: "") the chosen value
radio group missing the checked radio’s value
checkbox missing (never false) "on", or its value if set
text with defaultValue the default (title: "Love it") what the user typed
file the file, if one was picked —

So read a checkbox with formData.has('recommend'), and put a “Please choose” option first in a <select> unless a real value should be pre-selected.

How it works

Common mistakes

Interview Q&A

What's the difference between value and defaultValue?

value makes the input controlled: React sets it on every render, so the user can’t change it unless onChange updates the state behind it. Recorded: typing into <input value="Ceramic Mug" /> left it at "Ceramic Mug". defaultValue only sets the first value; the other input became "Ceramic Mug XL".

How do an unchecked checkbox and an empty radio group appear in FormData?

They don’t: their names are missing. The untouched review form submitted { productId, size: "", title } with no rating and no recommend. A checked checkbox sends "on" unless it has a value.

Why does a <select> always submit something, but a radio group doesn't?

A select always has a selected option (the first one by default), so it always contributes a value (size: "" above, from the placeholder option). Radios have no forced default.

Why can't you set value on a file input?

Browsers block it for security: a page must never choose a file for the user. Reset it with form.reset(), ref.current.value = '', or by remounting it with a new key.

Sources

Primary source: react.dev/reference/react-dom/components/input