[react-dom] Include `submitter` in `submit` events (#35590)
Sebastian "Sebbie" Silbermann committed
Feb 2, 2026 at 21:17 UTC
7b023d7073aa700b48674ae1985235b8c4755880
4 files changed
+49
-1
packages/react-dom-bindings/src/events/SyntheticEvent.js
+11
@@ -522,6 +522,17 @@ export const SyntheticPointerEvent: $FlowFixMe = createSyntheticEvent(
522
PointerEventInterface,
523
);
524
525
+/**
526
+ * @interface SubmitEvent
527
+ * @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#the-submitevent-interface
528
+ */
529
+const SubmitEventInterface: EventInterfaceType = {
530
+ ...EventInterface,
531
+ submitter: 0,
532
+};
533
+export const SyntheticSubmitEvent: $FlowFixMe =
534
+ createSyntheticEvent(SubmitEventInterface);
535
+
536
/**
537
* @interface TouchEvent
538
* @see http://www.w3.org/TR/touch-events/
packages/react-dom-bindings/src/events/plugins/SimpleEventPlugin.js
+4
@@ -27,6 +27,7 @@ import {
27
SyntheticWheelEvent,
28
SyntheticClipboardEvent,
29
SyntheticPointerEvent,
30
+ SyntheticSubmitEvent,
31
SyntheticToggleEvent,
32
} from '../../events/SyntheticEvent';
33
@@ -162,6 +163,9 @@ function extractEvents(
163
case 'pointerup':
164
SyntheticEventCtor = SyntheticPointerEvent;
165
break;
166
+ case 'submit':
167
+ SyntheticEventCtor = SyntheticSubmitEvent;
168
+ break;
169
case 'toggle':
170
case 'beforetoggle':
171
// MDN claims <details> should not receive ToggleEvent contradicting the spec: https://html.spec.whatwg.org/multipage/indices.html#event-toggle
packages/react-dom/src/__tests__/ReactDOMEventPropagation-test.js
+2
-1
@@ -672,9 +672,10 @@ describe('ReactDOMEventListener', () => {
672
reactEventType: 'submit',
673
nativeEvent: 'submit',
674
dispatch(node) {
675
- const e = new Event('submit', {
675
+ const e = new SubmitEvent('submit', {
676
bubbles: true,
677
cancelable: true,
678
+ submitter: null,
679
});
680
node.dispatchEvent(e);
681
},
packages/react-dom/src/events/plugins/__tests__/SimpleEventPlugin-test.js
+32
@@ -591,4 +591,36 @@ describe('SimpleEventPlugin', function () {
591
);
592
});
593
});
594
+
595
+ it('includes the submitter in submit events', async function () {
596
+ container = document.createElement('div');
597
+
598
+ const onSubmit = jest.fn(event => {
599
+ event.preventDefault();
600
+ });
601
+ const root = ReactDOMClient.createRoot(container);
602
+ await act(() => {
603
+ root.render(
604
+ <form onSubmit={onSubmit}>
605
+ <button type="submit" id="submitter">
606
+ Submit
607
+ </button>
608
+ </form>,
609
+ );
610
+ });
611
+
612
+ const submitter = container.querySelector('#submitter');
613
+ const submitEvent = new SubmitEvent('submit', {
614
+ bubbles: true,
615
+ cancelable: true,
616
+ submitter: submitter,
617
+ });
618
+ await act(() => {
619
+ submitter.dispatchEvent(submitEvent);
620
+ });
621
+
622
+ expect(onSubmit).toHaveBeenCalledTimes(1);
623
+ const event = onSubmit.mock.calls[0][0];
624
+ expect(event.submitter).toBe(submitter);
625
+ });
626
});