Skip to content

React Tutorial State and Events

Input Events in React: onChange, onFocus and onKeyDown

Lesson 50 of 156 2 min read By Deepak Paswan Updated Oct 10, 2026

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.

App.jsx
JSX
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

App.jsx
JSX
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

App.jsx
JSX
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:

App.jsx
JSX
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

App.jsx
JSX
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:

App.jsx
JSX
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

  • onChange fires on every change; read e.target.value (or e.target.checked).
  • Use onBlur to validate after the user leaves a field, and onKeyDown for Enter and Escape.
  • One handler with name can serve many inputs.