Himanshu SharmaSoftware engineer
Let’s talk

React · Lesson 13 of 38

Handling events

Respond to clicks, typing and submits in React: onClick and onChange handlers, passing arguments, the event object, preventDefault and event bubbling.

15 minState and eventsUpdated 24 October 2026

By the end of this lessonAttach event handlers to elements, pass the function (not its result), use the event object, and prevent the browser's default actions.

A page that cannot react to the user is a poster. React lets you attach event handlers to elements: functions that run when the user clicks, types, submits a form or moves the mouse. The syntax is close to HTML, with a few important differences. Handlers usually change state to update the screen, which is the next lesson; here we concentrate on how to attach and write them, and let the Console panel under each result show you what ran.

Adding a handler

Define a function inside your component and pass it to an element through an event prop such as onClick:

jsx
function App() {
  function handleClick() {
    console.log("The button was clicked");
  }

  return <button onClick={handleClick}>Click me</button>;
}

export default App;
result
Click me
CONSOLE
The button was clicked

The log line appears in the Console area each time you press the button. Notes:

  • Event prop names are camelCase: onClick, onChange, onSubmit, onKeyDown, onMouseEnter. Not onclick.
  • The handler is usually a function declared inside the component, named handle plus the event: handleClick, handleSubmit. Being inside the component lets it read the component's props and state.
  • A handler runs only when the event happens, never while the component renders. This is why event handlers are the right place for side effects.

You can also write the function inline when it is short:

jsx
function App() {
  return <button onClick={() => console.log("inline arrow")}>Click</button>;
}

export default App;
result
Click
CONSOLE
inline arrow

Pass the function, do not call it

The most common beginner bug is calling the function while writing the JSX:

jsx
function App() {
  function handleClick() {
    console.log("handleClick ran");
  }

  // Wrong: handleClick() runs while rendering. onClick receives its return value (undefined).
  return <button onClick={handleClick()}>Click me</button>;
}

export default App;
result
Click me
CONSOLE
handleClick ran
handleClick ran

The message appeared before anyone pressed the button, and clicking does nothing (twice in a row here, as StrictMode renders the component twice in development). With brackets you write the result of calling the function. Without brackets, onClick={handleClick}, you pass the function and React calls it later. The same goes for the inline form: write onClick={() => handleClick()}, an arrow function that calls it when the click happens.

Passing arguments to a handler

React calls your handler with one argument, the event. If you want to pass your own data, such as which item was clicked, wrap the call in an arrow function:

jsx
function App() {
  const fruits = ["Apple", "Banana", "Cherry"];

  function handleSelect(name) {
    console.log("You chose " + name);
  }

  return (
    <ul>
      {fruits.map(fruit => (
        <li key={fruit}>
          {fruit} <button onClick={() => handleSelect(fruit)}>Choose</button>
        </li>
      ))}
    </ul>
  );
}

export default App;
result
Apple Choose
Banana Choose
Cherry Choose
CONSOLE
You chose Banana

Each button gets its own arrow function that remembers its fruit. In the test I pressed the second button.

The event object

React passes your handler an event object that describes what happened. It is a wrapper around the browser's event, with the same useful properties, and works the same in every browser.

jsx
function App() {
  function handleChange(event) {
    console.log("typed:", event.target.value);
  }

  function handleClick(event) {
    console.log("tag:", event.target.tagName, "type:", event.type);
  }

  return (
    <div>
      <input onChange={handleChange} placeholder="Type here" />
      <button onClick={handleClick}>Inspect</button>
    </div>
  );
}

export default App;
result
Inspect
CONSOLE
typed: h
typed: hi
tag: BUTTON type: click

The properties you will use most:

Property or method Meaning
event.target the element where the event started (the input, the button)
event.target.value the current text of an input
event.target.checked whether a checkbox is ticked
event.currentTarget the element the handler is attached to
event.type the kind of event, such as "click"
event.key for keyboard events, the key pressed ("Enter")
event.preventDefault() cancels the browser's default action
event.stopPropagation() stops the event reaching parent handlers

React's onChange fires on every keystroke in a text input, like the browser's input event, not only when the field loses focus.

Preventing default behaviour

Some elements have a built-in action: a link navigates, and a form submit reloads the page. In a React app you usually take over, so cancel the default with preventDefault():

jsx
function App() {
  function handleSubmit(event) {
    event.preventDefault();              // do not reload the page
    console.log("form submitted, page not reloaded");
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" placeholder="Email" />
      <button type="submit">Subscribe</button>
    </form>
  );
}

export default App;
result
Subscribe
CONSOLE
form submitted, page not reloaded

Without preventDefault(), submitting would reload the page and everything on it would be lost. Put onSubmit on the <form>, not on the button, so that pressing Enter in the field also works. A form is covered in more depth in the Forms lessons.

Events bubble

When you click a button inside a div, the click happens on the button first and then bubbles up through its parent elements. Every ancestor with an onClick also runs, from the inside out:

jsx
function App() {
  return (
    <div onClick={() => console.log("div handler")} style={{ padding: 16, background: "#eee" }}>
      <button onClick={() => console.log("button handler")}>Click</button>
    </div>
  );
}

export default App;
result
Click
CONSOLE
button handler
div handler

Both handlers ran, button first. That is usually what you want, and it is what lets one handler on a list watch all its items. When you do not want the parent to hear about it, call event.stopPropagation() in the inner handler:

jsx
function App() {
  return (
    <div onClick={() => console.log("div handler")} style={{ padding: 16, background: "#eee" }}>
      <button onClick={event => { event.stopPropagation(); console.log("button handler only"); }}>
        Click
      </button>
    </div>
  );
}

export default App;
result
Click
CONSOLE
button handler only

Use stopPropagation sparingly; it can make behaviour surprising for other code that expects to hear about the click. If you need to react before children, React also supports capture handlers such as onClickCapture.

Handlers as props

A child component often needs to tell its parent that something happened. The parent passes a function down as a prop, and the child calls it from its own handler. By convention, the prop name starts with on and the function in the parent starts with handle:

jsx
function DeleteButton({ onDelete, label }) {
  return <button onClick={onDelete}>{label}</button>;
}

function App() {
  function handleDelete() {
    console.log("Parent: delete requested");
  }

  return <DeleteButton label="Delete item" onDelete={handleDelete} />;
}

export default App;
result
Delete item
CONSOLE
Parent: delete requested

This is how information flows up in React, a design you will use constantly: data goes down in props, and requests go up as function calls.

Other common events

Event prop Fires when
onClick, onDoubleClick the element is clicked
onChange an input's value changes (every keystroke)
onSubmit a form is submitted
onKeyDown, onKeyUp a key is pressed or released
onFocus, onBlur the element gains or loses focus
onMouseEnter, onMouseLeave the pointer enters or leaves the element
onScroll an element scrolls

A keyboard example: pressing Enter in a field runs an action.

jsx
function App() {
  function handleKeyDown(event) {
    if (event.key === "Enter") {
      console.log("Enter pressed");
    }
  }

  return <input onKeyDown={handleKeyDown} placeholder="Press Enter" />;
}

export default App;
result
CONSOLE
Enter pressed

Common mistakes

  • Calling the handler: onClick={handleClick()} instead of onClick={handleClick}.
  • Using the HTML spelling onclick, or a string: onClick="doSomething()".
  • Forgetting event.preventDefault() in a form handler, then wondering why the page reloads.
  • Putting onSubmit on the button instead of the form.
  • Putting side effects (logging, fetching) directly in the component body instead of in a handler.
  • Writing onClick={handle(id)} to pass an argument; wrap it: onClick={() => handle(id)}.
  • Overusing stopPropagation, which hides events from code that needs them.

Exercise

  1. Add a button that logs the current time (new Date().toLocaleTimeString()) when clicked.
  2. Make a text input that logs its value on every keystroke, then change it to log only when Enter is pressed.
  3. Write a list of three links. Use preventDefault() in the click handler and log which link was clicked instead of navigating.
  4. Put a button inside a div, give both handlers, and predict the order of the logs before you run it.

Key points

  • Attach handlers with props such as onClick, onChange and onSubmit, and pass a function: onClick={handleClick}, not onClick={handleClick()}.
  • Handlers are usually defined inside the component, named handleSomething, and passed to children as onSomething props.
  • The handler receives an event object: use event.target, event.preventDefault() and event.stopPropagation().
  • Events bubble from the clicked element up through its parents unless you stop them; handlers do not run during rendering.

Check yourself

1. What is wrong with <button onClick={handleClick()}>?
2. How do you pass an argument to a handler?
3. What does event.preventDefault() do in a form's onSubmit handler?
4. A button inside a div; both have onClick. Which handlers run when you click the button?