Why does React use event delegation instead of native listeners?

HighIntermediateReact
Preparing for interviews?

Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.

Quick Answer

Explain how React’s event system works (SyntheticEvent + root-level listeners) and why delegation is used: fewer listeners, easier updates for dynamic trees, consistent cross-browser behavior, and better integration with React’s scheduling/batching. Mention the React 17+ change (listeners attached to the root container instead of document) and common trade-offs (native vs synthetic, non-bubbling events).

Answer

Core idea

React usually doesn’t attach a native DOM listener to every element that has onClick/onChange, etc. Instead, it attaches a small set of listeners at the root container and uses event delegation: when the browser event bubbles up, React catches it once and dispatches it to the correct component handler via its internal tree.

Why delegation

What it buys React

What breaks with per-node native listeners

Fewer listeners (memory + setup cost)

One listener can cover thousands of nodes

Big trees would create many listeners and slow mounts/updates

Works with dynamic UIs

No attach/detach churn as elements appear/disappear during reconciliation

You’d constantly add/remove listeners on every commit

Consistent behavior (SyntheticEvent)

Normalizes event quirks across browsers + consistent API

Different native event edge cases leak into app logic

Tight integration with React scheduling

React can assign event priority (discrete/continuous) and coordinate updates

Harder to keep consistent ordering/priority if every node owns native listeners

Portals + component tree semantics

Dispatch follows React’s tree (including across portals) rather than only DOM structure

Handlers become harder to reason about when UI spans multiple DOM subtrees

Delegation is about scale + correctness, not just micro-performance

How it works (mental model)

1) Browser fires a native event on a target node.
2) The event bubbles up to the root container.
3) React’s root listener runs once, finds the closest React component instance for the target, then runs the matching handler(s) in the right order (capture/bubble).

JS
// Native DOM event delegation (concept)
const root = document.getElementById('root');

root.addEventListener('click', (e) => {
  const btn = e.target.closest('button[data-id]');
  if (!btn) return;

  console.log('Clicked id:', btn.dataset.id);
});

// React does something similar internally:
// - one (or few) listeners at the root
// - map DOM target -> React fiber/component
// - call the matching onClick handler
                  

React 17+ note

React attaches event listeners to the root container instead of the global document. This avoids interference when multiple React versions/roots coexist and keeps event handling scoped to a root.

Trade-off / gotcha

What you’ll notice

What to do

Native vs React events

e is a SyntheticEvent wrapper; native event is at e.nativeEvent

Use React handlers for UI updates; use native listeners in useEffect only when needed

Non-bubbling events

Some events don’t bubble in the DOM (React may emulate via capture or special handling)

Know when to use capture props (e.g., onFocusCapture) or native listeners

Propagation expectations

React propagation follows React’s tree semantics (including portals), not only DOM nesting

When debugging, think in “React tree”, not just “DOM tree”

Delegation is great, but understand the boundary with native DOM
Summary

React uses event delegation so it can handle events with a small number of root listeners, keep behavior consistent, avoid listener churn as the UI tree changes, and integrate event handling with React’s scheduling and update model. If it attached native listeners per element, large trees would pay heavy setup/teardown costs and React would lose control over consistent dispatch + prioritization.

Similar questions
Guides
13 / 41