Runnable lesson · press Try it on the examples to edit and run them here.
Inputs are where users type. React has a few events that help you react to every change, plus focus and keyboard events.
onChange fires on every keystroke
Unlike the plain DOM change event, which waits for the field to lose focus, React’s onChange fires on every change, the same as the DOM input event. That is why you can build live search and live validation easily.
function App() {
const [text, setText] = React.useState("");
return (
<div>
<input aria-label="Your text" value={text} onChange={(e) => setText(e.target.value)} />
<p>You typed: {text}</p>
<p>{text.length} characters</p>
</div>
);
}
e.target.value is the field’s current text. Connecting value and onChange makes it a controlled input, which has its own lesson: Controlled Inputs.
Checkboxes and radio buttons use checked
function App() {
const [subscribed, setSubscribed] = React.useState(false);
return (
<div>
<label>
<input type="checkbox" checked={subscribed} onChange={(e) => setSubscribed(e.target.checked)} />
Subscribe
</label>
<p>Newsletter is {subscribed ? "on" : "off"}</p>
</div>
);
}
For a checkbox, read e.target.checked, not e.target.value.
Select boxes
function App() {
const [colour, setColour] = React.useState("red");
return (
<div>
<label>
Colour
<select aria-label="Colour" value={colour} onChange={(e) => setColour(e.target.value)}>
<option value="red">Red</option>
<option value="blue">Blue</option>
</select>
</label>
<p>Chosen: {colour}</p>
</div>
);
}
In React you set value on the <select>, not selected on an <option>.
Focus and blur
onFocus runs when the field gets the cursor, onBlur when it loses it. A common use is to show validation only after the user leaves the field:
function App() {
const [email, setEmail] = React.useState("");
const [touched, setTouched] = React.useState(false);
const invalid = touched && !email.includes("@");
return (
<div>
<label>
Email <input aria-label="Email" value={email} onChange={(e) => setEmail(e.target.value)} onBlur={() => setTouched(true)} />
</label>
{invalid && <p role="alert" style={{ color: "#c0392b" }}>Please enter a valid email</p>}
<label>
Phone <input aria-label="Phone" />
</label>
</div>
);
}
The message appears only after you leave the Email field (click or tab to the Phone field). While you are still typing, it stays quiet.
React’s onFocus and onBlur bubble, unlike the native focus and blur events. That is useful for “focus within” behaviour on a whole group.
Enter and Escape
function App() {
const [items, setItems] = React.useState([]);
const [text, setText] = React.useState("");
function handleKeyDown(e) {
if (e.key === "Enter" && text.trim()) {
setItems([...items, text.trim()]);
setText("");
} else if (e.key === "Escape") {
setText("");
}
}
return (
<div>
<input aria-label="New item" placeholder="Add with Enter" value={text} onChange={(e) => setText(e.target.value)} onKeyDown={handleKeyDown} />
<ul>{items.map((i, n) => <li key={n}>{i}</li>)}</ul>
<p>Add with Enter</p>
</div>
);
}
Wrapping an input in a <form> with onSubmit is often better, because pressing Enter then submits it for free, including on mobile keyboards; see Form Handling.
Reading many inputs with one handler
Give each input a name and use it as a key in one handler:
function App() {
const [values, setValues] = React.useState({ first: "", last: "" });
function handleChange(e) {
const { name, value } = e.target;
setValues({ ...values, [name]: value }); // computed property name
}
return (
<div>
<input aria-label="first" name="first" value={values.first} onChange={handleChange} />
<input aria-label="last" name="last" value={values.last} onChange={handleChange} />
<p>first={values.first} last={values.last}</p>
</div>
);
}
[name] is a computed property name; see Computed Properties. This is covered further in Multiple Inputs.
Key points
onChangefires on every change; reade.target.value(ore.target.checked).- Use
onBlurto validate after the user leaves a field, andonKeyDownfor Enter and Escape. - One handler with
namecan serve many inputs.