React Event Propagation: The Complete Guide

Take charge of the DOM's event flow. Learn to stop bubbling with `e.stopPropagation()` and halt browser actions with `e.preventDefault()`.

Lesson ProgressStep 1 of 9
⚡️
0 EXP

Welcome! Let's explore how events travel through our components.

/* Starting our journey into the DOM event flow... */

What is Event Bubbling?

When an event happens on an element (like a click), it doesn't just stop there. It "bubbles up" to its parent, then its parent's parent, and so on, all the way to the `document`.

<div onClick={handleParentClick}>
  <button onClick={handleChildClick}>Click Me</button>
</div>

If you click the `button`, `handleChildClick` runs first, and then `handleParentClick` runs. This is event bubbling.

System Check

If you click the button in the example, which handler runs LAST?

Advanced Holo-Simulations

0 EXP

Log in to unlock these advanced training modules and test your skills.


Achievements

🏆
Bubble Master

Correctly identify the order of event bubbling.

🏗️
Propagation Stopper

Use e.stopPropagation() to halt bubbling.

✍️
Default Preventer

Use e.preventDefault() to stop a default action.

Mission: Build a Click-Safe Modal

Fix this modal. A click on the gray `.overlay` should close it (which it does), but a click on the white `.modal-content` should **not**. You must stop the click from bubbling up.

A.D.A. Feedback:

> Awaiting input...

Challenge: Match the Method

Drag the correct event method to its description.

e.preventDefault()
e.stopPropagation()
Stops an event from bubbling up to parent elements.
Stops the browser's default action (e.g., form submit, link navigation).

Challenge: Complete the Syntax

Fill in the blank to stop this form from reloading the page when submitted.

function MyForm() {
  const handleSubmit = (e) => {
    // Stop the page reload
    ;
    console.log("Handling submit in React!");
  }

  return <form onSubmit={handleSubmit}>...</form>
}

Consult A.D.A.

Community Holo-Net

Beyond the Click: Mastering React's Event System

In React, handling events like `onClick` seems simple. But to build complex, bug-free applications, you must understand what's happening under the hood. When an event fires, it doesn't just happen in one place; it travels through the DOM. Mastering this "event flow" and knowing how to control it with methods like **`e.stopPropagation()`** and **`e.preventDefault()`** is the difference between a novice and a professional developer.

First, it's important to know that React doesn't attach event listeners to every single DOM node you render. For performance, React uses a technique called **event delegation**, attaching a single event listener for each event type at the root of your application. When you click a button, the event bubbles up to this root listener, which then dispatches it to your component's `onClick` handler. React wraps the native browser event in a **`SyntheticEvent`** object to ensure consistent behavior across all browsers.


The 3 Phases of DOM Event Flow

Every event in the DOM goes through three phases:

  1. Capturing Phase: The event travels *down* from the `window` and `document` root to the target element.
  2. Target Phase: The event reaches the element that was directly interacted with (e.g., the button that was clicked).
  3. Bubbling Phase: The event travels *up* from the target element, through all its parents, back to the `window`.

By default, React's event handlers (`onClick`, `onChange`, etc.) listen during the **bubbling phase**, which is the most intuitive and commonly used.


Controlling the Flow: `e.stopPropagation()`

This is your "stop" button for the bubbling phase. When you call `e.stopPropagation()` inside an event handler, you are telling the browser: "This event has been handled. Do not let it travel any further up the DOM tree."

Real-World Example 1: The Modal Overlay

This is the classic use case. You have a modal with a gray overlay. Clicking the overlay should close the modal, but clicking the modal's content should do nothing.

function Modal({ onClose }) {
  const handleContentClick = (e) => {
    // Stop the click from bubbling up to the overlay
    e.stopPropagation();
  }

  return (
    <div className="overlay" onClick={onClose}>
      <div className="modal-content" onClick={handleContentClick}>
        {/* If we didn't stop propagation, a click here
            would bubble to the overlay and trigger onClose! */}
        <p>Modal Content</p>
      </div>
    </div>
  );
}

Real-World Example 2: Nested Clickable Elements

Imagine a clickable `Card` component that links to a detail page. Inside that card, you have a "Delete" button. If a user clicks "Delete," you only want to delete the item, not *also* navigate to the detail page.

function Card({ onNavigate }) {
  const handleDeleteClick = (e) => {
    // Stop the click from bubbling to the Card's onClick
    e.stopPropagation();
    // ...run delete logic
    console.log("Item deleted!");
  }

  return (
    <div className="card" onClick={onNavigate}>
      <h3>Card Title</h3>
      <p>Some content...</p>
      <button onClick={handleDeleteClick}>Delete</button>
    </div>
  );
}

Controlling the Browser: `e.preventDefault()`

This method is completely different. It does **not** stop propagation. Instead, it tells the browser: "Do not perform your default action for this event."

What are default actions?

  • A `<form>` being submitted (which causes a full page reload).
  • An `<a>` tag being clicked (which navigates to a new URL).
  • Pressing the spacebar (which scrolls the page down).
  • Dragging an element (which has a default "ghost" image).

Real-World Example: Handling Form Submissions

This is the most common use of `e.preventDefault()`. In a React app, you *never* want a form to cause a full page reload. You want to handle the data, send it to an API, and update the state, all within your application.

function MyForm() {
  const handleSubmit = (e) => {
    // THIS IS THE MOST IMPORTANT LINE!
    e.preventDefault();
    
    // Now we can handle the submit with JavaScript
    console.log("Form submitted without page reload!");
    // ...send data to an API
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" />
      <button type="submit">Submit</button>
    </form>
  );
}

Advanced Controls: `onClickCapture` and `stopImmediatePropagation`

`onClickCapture`: If you need to catch an event during the *capturing* phase (as it travels *down*), React provides a special prop. An event handler on `onClickCapture` will always run *before* any `onClick` handler on elements nested inside it. This is rare but useful for high-level event logging or for stopping an event before it even reaches its target.

`e.stopImmediatePropagation()`: This is the "nuclear option." It does everything `e.stopPropagation()` does (stops bubbling), but it *also* stops any other event listeners *on the same element* from running. This is almost never needed, but it's good to know it exists.

Key Takeaway: Use **`e.preventDefault()`** to stop the browser's default behavior. Use **`e.stopPropagation()`** to stop an event from bubbling up to parent elements. Mastering these two methods gives you complete control over user interactions.

React Event Glossary

SyntheticEvent
React's cross-browser wrapper around the browser's native event object. It provides a consistent API (like `e.preventDefault()`) that works identically in all browsers.
Event Propagation
The process of how an event travels through the DOM tree. It consists of three phases: Capturing (down), Target (at the element), and Bubbling (up).
Event Bubbling
The third phase of event propagation, where the event "bubbles up" from the target element through its parent, grandparent, and so on, up to the `window`. This is the default phase React handlers use.
Event Capturing
The first phase of event propagation, where the event travels *down* from the `window` to the target element. You can listen for this in React by using `onClickCapture` instead of `onClick`.
`e.stopPropagation()`
A method on the `SyntheticEvent` object. When called, it prevents the event from continuing its journey up (or down) the DOM tree. It stops bubbling *and* capturing.
`e.preventDefault()`
A method on the `SyntheticEvent` object. When called, it signals to the browser not to perform its default action for that event (e.g., don't submit a form, don't follow a link). It does **not** stop propagation.
Event Delegation
The performance technique React uses. Instead of attaching thousands of `onClick` listeners, React attaches one listener at the root of the app. Events bubble up to it, and React then calls your specific component's handler.
`e.target`
A property on the event object that refers to the DOM element that *originated* the event (e.g., the specific `<span>` inside a button you clicked).
`e.currentTarget`
A property on the event object that refers to the DOM element that the event listener is *currently attached to*. In React, this usually refers to the element you put your `onClick` prop on.
`e.stopImmediatePropagation()`
An advanced method that does two things: 1) It stops propagation like `e.stopPropagation()`. 2) It prevents any *other* event listeners on the *same element* from running.

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of web development experts, who have years of experience teaching React and building robust, accessible web applications.

Verification and Updates

Last reviewed: October 2025.

We strive to keep our content accurate and up-to-date. This tutorial is based on the latest React specifications and is periodically reviewed to reflect industry best practices.

External Resources

Found an error or have a suggestion? Contact us!