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}oronClick={() => fn(id)} - Events bubble through the React tree;
e.stopPropagation()ande.preventDefault()work as in the DOM - Name handler props
onXand implementationshandleX; side effects belong in handlers
Watch out for
- Writing
onClick={remove(id)}, which callsremoveon every render instead of on click.