React Events Cross-Browser Virtual DOM Friendly
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.
preventDefault() and stopPropagation().nativeEvent.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.In React, the event object you receive in an event handler is a SyntheticEvent, not the raw browser event:
// 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>
);
}
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.
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.
// 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"
/>
);
}
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 and synthetic events share many properties, but differ in scope, compatibility, and how they’re managed:
nativeEvent.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.
// 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>
);
}
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.
e.persist()).nativeEvent only when you truly need low-level browser-specific details.onClick, onChange) instead of manually calling addEventListener() on DOM nodes inside components.e.type, e.target, and e.nativeEvent to understand how React wraps native events.setTimeout() and read e.target.value after 2 seconds to confirm that SyntheticEvents work asynchronously in React 18.<div> and handle all clicks with a single parent onClick handler (event delegation). Log which item was clicked.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.