← Back to Chapters

Synthetic Events in React

⚛️ Synthetic Events in React

React Events Cross-Browser Virtual DOM Friendly

? Quick Overview

React’s SyntheticEvent is a cross-browser wrapper around the browser’s native event object. It normalizes event behavior so your React app behaves consistently in Chrome, Firefox, Safari, Edge, and more.

Synthetic events integrate tightly with React’s virtual DOM system, making event handling predictable, performant, and easier to reason about than manually wiring native DOM events.

? Key Concepts

  • SyntheticEvent is React’s abstraction over the native browser event.
  • It provides a unified API with familiar methods like preventDefault() and stopPropagation().
  • Before React 17, event pooling reused event objects for performance; pooling is now removed.
  • React uses event delegation, attaching a small number of listeners at the root instead of on every node.
  • Every SyntheticEvent exposes both high-level properties and the underlying nativeEvent.

? Syntax & Theory

A SyntheticEvent instance is passed to your event handlers in React components. It behaves like a regular browser event, but with consistent properties and methods across all supported browsers.

Common properties and methods of a SyntheticEvent include:

  • type — The event type (e.g., "click", "change").
  • target — The element that originally triggered the event.
  • currentTarget — The element the event handler is currently attached to.
  • timeStamp — The time at which the event occurred.
  • nativeEvent — The underlying native browser event object.
  • preventDefault() — Prevents the default browser behavior.
  • stopPropagation() — Stops the event from bubbling up the DOM tree.
  • isPropagationStopped() — Checks whether propagation was stopped.
  • isDefaultPrevented() — Checks whether the default action was prevented.

? Code Example: Basic SyntheticEvent

In React, the event object you receive in an event handler is a SyntheticEvent, not the raw browser event:

? View Code Example
// Basic example using React SyntheticEvent
function Example() {
  const handleClick = (e) => {
    console.log("Synthetic event type:", e.type);
    console.log("Is default prevented?", e.isDefaultPrevented());
  };

  return (
    <button className="btn btn-primary" onClick={handleClick}>
      Click Me
    </button>
  );
}

? Explanation

When the button is clicked, React passes a SyntheticEvent instance as e. You can inspect its type, call preventDefault(), or check flags like isDefaultPrevented() just as you would with a native event — but with consistent behavior across browsers.

? Event Pooling (Before React 17)

Prior to React 17, React used event pooling to improve performance by reusing event objects. After your event handler finished, the event’s properties were cleared, so accessing the same event asynchronously could lead to unexpected null / empty values.

? View Code Example
// Example showing how persist() was used with pooled events
function InputLogger() {
  const handleChange = (e) => {
    e.persist(); // prevents pooling on this event instance

    setTimeout(() => {
      console.log("Input value:", e.target.value); // safely accessible after 1 second
    }, 1000);
  };

  return (
    <input
      className="form-control w-50"
      type="text"
      onChange={handleChange}
      placeholder="Type and wait 1s"
    />
  );
}

✅ React 17+ Behavior

In React 17 and later, event pooling was removed. You no longer need e.persist(), and you can freely use event objects in async code like setTimeout() or promises without worrying about the event being cleared.

? Native vs Synthetic Event

Native and synthetic events share many properties, but differ in scope, compatibility, and how they’re managed:

? Comparison Overview

  • Compatibility: Synthetic events normalize quirks between browsers.
  • Performance: React uses event delegation plus batching for efficient updates.
  • Scope: Synthetic events live inside React’s virtual DOM system.
  • Access: You can still reach the underlying native event via nativeEvent.

? Synthetic Event Delegation

React attaches a small number of event listeners at the root (such as on the document) and uses event delegation plus bubbling to handle events from many child elements. This reduces the number of listeners and improves performance.

? View Code Example
// React handles many child button clicks using a single parent handler
function ManyButtons() {
  const handleClick = (e) => {
    console.log("Clicked:", e.target.textContent);
  };

  return (
    <div onClick={handleClick}>
      <button className="btn btn-outline-primary m-1">One</button>
      <button className="btn btn-outline-success m-1">Two</button>
      <button className="btn btn-outline-danger m-1">Three</button>
    </div>
  );
}

? What Happens Here?

Even though there are three buttons, only the parent <div> has an event handler. The click event bubbles from the button to the parent, and React’s delegated listener calls handleClick. You can still read which button was clicked via e.target.

? Tips & Best Practices

  • You can safely use SyntheticEvents asynchronously in React 17+ (no need for e.persist()).
  • Use nativeEvent only when you truly need low-level browser-specific details.
  • Leverage React’s event delegation by attaching handlers higher in the tree when appropriate.
  • Always use React’s event system (e.g., onClick, onChange) instead of manually calling addEventListener() on DOM nodes inside components.
  • Remember that SyntheticEvents are automatically cleaned up by React, helping prevent memory leaks.

? Try It Yourself

  1. Create a component with a button that logs e.type, e.target, and e.nativeEvent to understand how React wraps native events.
  2. Inside a React event handler, use setTimeout() and read e.target.value after 2 seconds to confirm that SyntheticEvents work asynchronously in React 18.
  3. Build a list of buttons inside a parent <div> and handle all clicks with a single parent onClick handler (event delegation). Log which item was clicked.
  4. Compare behavior by wiring up a native addEventListener() on a DOM element and contrast it with React’s onClick handler on the same element.

Goal: Understand how React’s SyntheticEvent system normalizes browser events, supports asynchronous usage, and improves performance via event delegation — all while still giving you access to the underlying native event when needed.