You need a property that React's SyntheticEvent does not expose — for example the submitter of a form's submit event. How do you decide when to drop to e.nativeEvent, and what do you take on when you do?
answer
- React normalizes a curated subset
- the rest is still reachable
- the raw object comes with raw guarantees
- confirm what actually backs the event
- e.nativeEvent
basics
~20 sReact normalizes a curated subset of each event's properties; anything outside it lives on e.nativeEvent, which is the browser's own object. Reading it is supported and normal, but you inherit raw browser behaviour, so you own the compatibility check.
solid answer
~50 sReact's synthetic events expose a deliberately curated surface — the members React normalizes across browsers. A form submit event, for instance, gives you the base surface but not `submitter`, so you read `e.nativeEvent.submitter`. That is a documented, supported escape hatch, not an internal. The trade is what you inherit: `nativeEvent` is the browser's own object, so nothing about it is normalized, and support is whatever that engine offers — you check it yourself. Two further judgments matter. First, verify *which* native event actually backs the synthetic one: React's `onChange` is backed by a native `input` event, and `onFocus` by `focusin`, so `e.nativeEvent` may not be the type you assumed. Second, prefer the normalized property whenever one exists — reaching down when you did not have to is how a component quietly acquires a browser dependency.
code
javascript · 15 linesfunction ActionForm({ onSave, onDelete }) {
function handleSubmit(e) {
e.preventDefault();
const submitter = e.nativeEvent.submitter; // React does not normalize this
if (submitter?.value === 'delete') onDelete();
else onSave();
}
return (
<form onSubmit={handleSubmit}>
<button name="action" value="save">Save</button>
<button name="action" value="delete">Delete</button>
</form>
);
}go deeper
Know that e.nativeEvent exists and is the supported way to reach a property React's event does not expose, such as a submit event's submitter.
Explain that React normalizes a curated subset by design, and that reading nativeEvent means reading the raw browser object with no normalization behind it.
Demonstrate the check before the reach: confirm which native event actually backs the synthetic one, verify support in your target browsers, and prefer the normalized property whenever one exists.
Own it as a coupling decision — every nativeEvent read is an undeclared platform dependency inside a component, so decide where such reads are allowed to live and how they are documented and tested.
## Why the gap exists at all React copies a defined list of properties from the browser's event onto the SyntheticEvent — the ones it is willing to guarantee behave identically everywhere. A mouse event gets `clientX`, `clientY`, `button`, `buttons`, `shiftKey`, `metaKey`, `relatedTarget` and friends; a keyboard event gets `key`, `code`, `repeat`, `getModifierState()`; every event gets the base members `type`, `target`, `currentTarget`, `bubbles`, `cancelable`, `eventPhase`, `isTrusted`, `timeStamp`. Anything not on that list is not missing by accident. It is simply outside the normalized contract, and React hands you the original object so you can get it yourself. ## The concrete case: which button submitted A form with two submit buttons is the standard example. React's `onSubmit` gives you the base surface; the DOM's `SubmitEvent.submitter` — the element that triggered the submission — is not part of it: ```js function handleSubmit(e) { e.preventDefault(); const submitter = e.nativeEvent.submitter; if (submitter?.value === 'delete') { /* ... */ } } ``` The IME case is similar. A keyboard handler that must ignore Enter while a composition is in progress reads `e.nativeEvent.isComposing`, because React's keyboard surface does not carry it. Without that check, a CJK input's Enter to confirm a candidate is mistaken for "submit". ## The decision procedure When you find yourself reaching for `nativeEvent`, work through four questions. **1. Is there a normalized property that already does this?** If React exposes it, use React's. A component that reads normalized properties keeps working when React changes how it dispatches; one built on the native object is coupled to the browser. **2. Which native event is actually under there?** This trips people up. The synthetic event's name does not always match the native one. React's `onChange` on a text input is backed by the native `input` event, not `change`; `onFocus` and `onBlur` are backed by `focusin`/`focusout` so that they bubble. If you assume the native type, you will look for properties that are not on it. Log `e.nativeEvent.type` once and confirm. **3. What is the support story?** `nativeEvent` is the raw platform. `SubmitEvent.submitter` and `isComposing` are widely supported today, but neither React nor your build tooling polyfills what you read there. Check it, and write the fallback (`submitter ?? null`) rather than assuming. **4. Could a ref with your own listener be the honest answer instead?** If you need a native property *and* native listener semantics, sometimes the right move is an explicit listener on a ref rather than mining React's event. That is a bigger decision with its own trade-offs, but it deserves to be on the table rather than reached for by reflex. ## What you take on Three things, concretely. *Compatibility becomes yours.* React's promise is about the normalized surface. Anything on `nativeEvent` behaves however that engine behaves, and differences will show up as user reports, not build errors. *Type safety weakens.* In TypeScript, `nativeEvent` on a generic handler is typed as the base `Event` for many handlers, so you end up narrowing or casting — and a cast is a claim you have to be right about, which is exactly where a wrong assumption about the underlying event type turns into a runtime `undefined`. *The dependency is invisible.* Nothing about `e.nativeEvent.submitter` announces that this component now depends on a platform feature. A short comment naming the feature and why the normalized surface was insufficient pays for itself. ## Where it is clearly right None of this argues against using it. `nativeEvent` is public, documented, and the sanctioned answer to "React does not expose X." Reading `submitter`, checking `isComposing`, inspecting clipboard or drag details beyond what React normalizes — all legitimate. The senior signal is not refusing the escape hatch; it is using it deliberately, having checked what actually backs the event and what happens in the browsers you support.
- Why is checking e.nativeEvent.type sometimes a necessary first step?Because the synthetic event's name is not always the native one. React's onChange on a text input is backed by the native `input` event and onFocus by `focusin`, so a property you expect on the same-named native event may simply not be there. Logging the native type once tells you which interface you are actually reading and stops a wrong assumption becoming a runtime undefined.
- Someone proposes bypassing React events entirely and adding their own listeners via refs for consistency. How do you push back?You lose the normalization React gives you for free — the smoothed-over inconsistencies, the phase props, the uniform interface — and you take on registration and cleanup by hand for every element. An escape hatch used at a few specific points is cheap; making it the house style multiplies the surface where lifecycle bugs live, for no gain on the properties React already normalizes.
- How does this look in TypeScript?React's handler types give `nativeEvent` the interface React can guarantee, which for many handlers is the base `Event` — so reading a specific property means narrowing or casting. Prefer a runtime check (`'submitter' in e.nativeEvent`) or an `instanceof` narrow over a bare cast: the cast asserts something the compiler cannot verify, and it is exactly the assumption most likely to be wrong.
saying these in an interview costs you the question
- Calls nativeEvent a private React internal
- Assumes the native event type matches the prop name
- Expects React to polyfill what nativeEvent exposes
- Casts nativeEvent in TypeScript without any runtime check
- Uses nativeEvent even where React normalizes the property