Responding to events

React · Essentials · 10 min

Events are how users interact with your UI and the trigger for nearly every state change.

You handle events by passing a function to a camelCase prop such as onClick, onChange, onSubmit or onKeyDown:

<button onClick={handleClick}>Save</button>

Pass the function, don't call it. onClick={handleClick()} runs during render and passes its return value. When you need arguments, wrap it: onClick={() => deleteItem(id)}.

Handlers receive a synthetic event that wraps the native one with the same interface across browsers: e.target, e.preventDefault(), e.stopPropagation(), e.key. Events bubble up through the React tree just like in the DOM, so a click on a button also reaches an onClick on its parent unless you stop propagation. (React attaches its listeners at the root, not on each element.)

A very common pattern is passing handlers as props. The parent decides what happens; the child decides when:

function Toolbar({ onPlay }) {
  return <button onClick={onPlay}>Play</button>;
}

By convention, props that take handlers are named onSomething and the functions that implement them handleSomething.

For forms, call e.preventDefault() in an onSubmit handler to stop the browser's full-page submit, or use React 19's action prop, covered later. Handlers are where side effects belong: they may update state, call APIs and log analytics, because they run in response to a user action rather than during rendering.

Example

function Button({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}

function Toolbar({ onPlayMovie, onUploadImage }) {
  return (
    <div className="toolbar" onClick={() => console.log("toolbar clicked")}>
      <Button onClick={onPlayMovie}>Play movie</Button>
      <Button
        onClick={e => {
          e.stopPropagation();      // the toolbar handler won't run
          onUploadImage();
        }}
      >
        Upload image
      </Button>
    </div>
  );
}

export default function App() {
  return (
    <Toolbar
      onPlayMovie={() => alert("Playing!")}
      onUploadImage={() => alert("Uploading!")}
    />
  );
}

Remember

  • Pass a function to onClick, don't call it: onClick={fn} or onClick={() => fn(id)}
  • Events bubble through the React tree; e.stopPropagation() and e.preventDefault() work as in the DOM
  • Name handler props onX and implementations handleX; side effects belong in handlers

Watch out for

  • Writing onClick={remove(id)}, which calls remove on every render instead of on click.

Check yourself

  1. What is wrong with <button onClick={setOpen(true)}>Open</button>?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: react.dev/learn/responding-to-events