In React, onChange is the event prop that fires as the user edits an input, picks an option, or toggles a checkbox. It is the idiomatic way to keep a controlled field in sync with state, and it fires on every edit rather than waiting for blur. The prop name differs from the native DOM event, but in React onChange is the one you almost always want.
Control a text input
A controlled input gets its value from React state and reports edits back through onChange. The field shows exactly what state holds, so React stays the single source of truth, and you can read or transform the value at any time without asking the DOM.
import { useState } from "react";
function EmailField() {
const [email, setEmail] = useState("");
return (
<label>
Email
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
</label>
);
}Typing updates state on every keystroke, and the input echoes the value back. The event target is the input element, so e.target.value holds the latest text. Because the state updates synchronously in the handler, the field never falls out of step with what React renders.
Checkboxes use checked
Checkboxes do not use value. Pass the checked prop and read e.target.checked in the handler. The checked prop holds a boolean, so the field can only be on or off.
import { useState } from "react";
function ConsentCheckbox() {
const [agreed, setAgreed] = useState(false);
return (
<label>
<input type="checkbox" checked={agreed} onChange={(e) => setAgreed(e.target.checked)} />
I agree to the terms
</label>
);
}Clicking the box flips the state, and the box reflects the new value. Reading e.target.checked instead of e.target.value is the rule that catches most checkbox bugs, because value on a checkbox is a fixed string rather than the on or off state.
Selects use value like inputs
A select element works the same way as a text input. Put onChange on the select and read e.target.value, which matches the selected option's value attribute. The options stay ordinary HTML; only the select itself carries the controlled prop.
import { useState } from "react";
function PlanPicker() {
const [plan, setPlan] = useState("free");
return (
<select value={plan} onChange={(e) => setPlan(e.target.value)}>
<option value="free">Free</option>
<option value="pro">Pro</option>
</select>
);
}Choosing Pro sets plan to pro, and the select reopens on that option. Textareas follow the text input pattern: pass value and onChange, and the text content is controlled by the value prop rather than the children.
How the three controls differ
The controlled prop and the event property to read change from one control to the next.
| Control | Controlled prop | Read from the event |
|---|---|---|
| Text input and textarea | value | e.target.value |
| Checkbox | checked | e.target.checked |
| Select | value | e.target.value |
onChange fires on every edit
React fires onChange immediately on each change, which matches the browser's input event more closely than the native change event that waits for blur. The onInput prop also exists and behaves similarly, but onChange is the idiomatic choice in React and what most examples and libraries use. A change handler can therefore run before the user leaves the field, so validation can respond as the user types.
Rules that trip people up
A few rules cover most debugging time when a controlled field misbehaves.
- Passing value without onChange freezes the field, because React keeps forcing the old value on every render.
- A checkbox needs checked, not value.
- value must stay a string. Start empty state as an empty string, not null or undefined.
- An uncontrolled field uses defaultValue or defaultChecked instead, and reads values only when needed.
- Updating state asynchronously can make the caret jump, so set the value synchronously in the handler.
The uncontrolled approach is the topic of the FormData guide, which is useful when you do not need to track every keystroke.
What to learn next
Handlers often need to know which field changed, and a handler shared by several fields can read the name from the event target to tell them apart. Typing event handlers adds the types once TypeScript enters the project, and the event handling overview covers the click side of the same pattern.
Rune AI
Key Insights
- onChange fires on every edit, not only on blur.
- Text inputs and selects use the value prop.
- Checkboxes use checked and read e.target.checked.
- A controlled field needs onChange or it becomes read-only.
- Keep value a string and start empty state as an empty string.
Frequently Asked Questions
Does React onChange fire on blur or on every keystroke?
Why does my input not update when I type?
What should I read for a checkbox?
Conclusion
onChange keeps a controlled field in sync with React state. Pass value for text inputs and selects, checked for checkboxes, and read the matching property from the event target.
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.