Menu

React Events: onClick, onChange, onSubmit and More

React events are props like onClick and onChange that take a function, which React calls with an event object when the user acts. Learn how to pass a handler instead of calling it, pass arguments, read e.target.value and e.key, prevent form reloads and stop bubbling.

This page includes runnable editors - edit, run, and see output instantly.

React handles events with props named after the event in camelCase, such as onClick, onChange and onSubmit. You pass a function, and React calls it with an event object when the user clicks, types or submits.

Each click runs handleClick, which updates the count and logs to the console under the preview. Try onDoubleClick in place of onClick: now only a double click counts.

Pass the function, do not call it

The prop takes a function, and React calls it later. A very common mistake is adding parentheses, which calls the function right away, during render, and hands React its return value instead.

The console shows "Hi, during render" before you touch anything: React ran sayHi while rendering the first button, and since sayHi returns undefined, that button has no handler at all. Clicking Wrong does nothing. Clicking Right logs "Hi, Ada", because the arrow function only runs on click.

The arrow is also how you pass arguments. onClick={() => removeItem(item.id)} creates a small function for each item, and that function calls yours with the right ID. If a handler takes no arguments, pass it by name: onClick={handleClick}.

With a state setter the mistake is worse: onClick={setCount(count + 1)} sets state during render, which renders again, which sets state again, and React stops with "Too many re-renders".

The event object

React passes every handler one argument, the event object. It has the same properties as the browser's event: e.target is the element the event happened on, e.key is the key that was pressed, e.preventDefault() cancels the browser's default action. React wraps the native event in what it calls a synthetic event, so it behaves the same in every browser; the original is at e.nativeEvent if you ever need it.

The two you use most are e.target.value in onChange and e.key in onKeyDown.

Type hooks and press Enter: the tag is added and the field clears. The console logs every key, including Shift, Backspace and Enter, by the names in e.key. Press Escape halfway through a word to throw it away.

React's onChange fires on every keystroke. In plain HTML the change event on a text field fires only when it loses focus, so this is a deliberate difference: it lets the input and state stay in step letter by letter. Checkboxes give you e.target.checked instead of value. The forms page covers every input type.

Forms and preventDefault

When a form is submitted, the browser's default action is to send it and load a new page. In a React app you almost always want to handle the data yourself, so the submit handler starts with e.preventDefault().

Put onSubmit on the <form>, not onClick on the button: then pressing Enter in the field submits too, and the browser's own checks (here required and type="email") run first. Delete the e.preventDefault() line and submit again: a real page would now leave for a new page and lose the thanks message. The preview blocks that and shows what the form would send instead, email= with no value, because setEmail('') had already cleared the field when the form was read.

React 19 also lets you pass a function to <form action={fn}>, which receives the form data and resets uncontrolled fields for you. The forms page shows both styles.

Bubbling and stopPropagation

Most events bubble: after running on the element you clicked, they run on its parent, then on the parent's parent, up to the top. React handlers follow the same order. That is why a click on a button inside a clickable card also counts as a click on the card.

Click Bubbles up and the console logs the button, then the inner panel, then the outer card. Click Stops here and only the button logs: e.stopPropagation() ends the climb. Clicking the inner panel's own text logs the panel and the card.

Use stopPropagation sparingly. A common legitimate case is a delete button inside a clickable list row, where clicking delete should not also open the row. If a parent needs to know whether the click came from a child, check e.target instead of stopping the event. To run a handler on the way down instead, before children see the event, React offers capture versions such as onClickCapture.

preventDefault and stopPropagation are different tools. The first cancels what the browser would do (follow a link, submit a form, tick a checkbox). The second stops other handlers from hearing about the event. Calling one does not do the other.

Other common events

The same pattern covers every event React supports. A few you will use often:

  • onFocus and onBlur fire when an input gains and loses focus, handy for showing validation after the user leaves a field.
  • onMouseEnter and onMouseLeave fire when the pointer moves onto and off an element. Unlike most events they do not bubble, so moving the pointer from a parent into its child does not fire the parent's onMouseEnter again.
  • onPointerDown, onPointerMove and onPointerUp work for mouse, touch and pen alike, which makes them the better choice for dragging.
  • onScroll fires as an element scrolls. In React it does not bubble.

Each handler receives an event object with the properties that make sense for it: e.clientX for pointer events, e.key for keyboard events, e.target for all of them.

Passing handlers as props

Components can accept handlers from their parent like any other prop. Name them onSomething after what happened, and name the function that handles it handleSomething. This mirrors the built-in events and makes it obvious which props are callbacks.

Stepper does not own the number; it reports clicks and the parent decides what they mean. Change handleDecrement to stop at 1 instead of 0 and the stepper follows without being edited. Keeping state in the parent and passing handlers down is the pattern described in lifting state up.

Custom handler props are only names. onIncrement on your own component does nothing until the component passes it to a real element's onClick, and you can give it any arguments you like, for example onSelect(item.id).

Frequently Asked Questions

How do I pass a parameter to an onClick handler?

Wrap the call in an arrow function: onClick={() => deleteItem(item.id)}. The arrow is what React calls on click, and it then calls your function with the argument.

Why does my onClick run when the page loads?

You called the function instead of passing it. onClick={handleClick()} runs handleClick during render and passes its return value. Write onClick={handleClick} or onClick={() => handleClick(id)}.

What is the difference between onChange in React and in HTML?

In HTML the change event on a text input fires when the input loses focus. React's onChange fires on every keystroke, like the native input event, so state can follow the text as it is typed.

How do I stop a form from reloading the page in React?

Call e.preventDefault() at the start of the onSubmit handler. Put the handler on the <form>, not on the submit button, so pressing Enter in a field submits too.

What is a synthetic event in React?

It is the event object React passes to your handlers. It wraps the browser's native event with the same properties and methods (target, key, preventDefault(), stopPropagation()) and behaves the same across browsers. The original is available as e.nativeEvent.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED