facebook/react · error · Error
A React form was unexpectedly submitted. If you called form.
Error message
A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you're trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().
What it means
When a <form> has a function action (Server Action or form action prop), React cannot put a real URL in the action attribute, so it writes action="javascript:throw new Error('A React form was unexpectedly submitted...')" as a placeholder. React normally preventDefaults the submit event so the URL never runs; the error only fires when the browser actually navigates to that javascript: URL because React's handler did not get the event.
Source
Thrown at packages/react-dom-bindings/src/client/ReactDOMComponent.js:562
domElement.setAttribute(key, sanitizedValue);
break;
}
case 'action':
case 'formAction': {
// TODO: Consider moving these special cases to the form, input and button tags.
if (__DEV__) {
validateFormActionInDevelopment(tag, key, value, props);
}
if (typeof value === 'function') {
// Set a javascript URL that doesn't do anything. We don't expect this to be invoked
// because we'll preventDefault, but it can happen if a form is manually submitted or
// if someone calls stopPropagation before React gets the event.
// If CSP is used to block javascript: URLs that's fine too. It just won't show this
// error message but the URL will be logged.
domElement.setAttribute(
key,
// eslint-disable-next-line no-script-url
"javascript:throw new Error('" +
'A React form was unexpectedly submitted. If you called form.submit() manually, ' +
"consider using form.requestSubmit() instead. If you\\'re trying to use " +
'event.stopPropagation() in a submit event handler, consider also calling ' +
'event.preventDefault().' +
"')",
);
break;
} else if (typeof prevValue === 'function') {
// When we're switching off a Server Action that was originally hydrated.
// The server control these fields during SSR that are now trailing.
// The regular diffing doesn't apply since we compare against the previous props.
// Instead, we need to force them to be set to whatever they should be now.
// This would be a lot cleaner if we did this whole fork in the per-tag approach.
if (key === 'formAction') {
if (tag !== 'input') {
// Setting the name here isn't completely safe for inputs if this is switching
// to become a radio button. In that case we let the tag based override take
// control.View on GitHub (pinned to eafeac097b)
Solutions
- Replace form.submit() with form.requestSubmit() — it dispatches a real submit event React can intercept and preventDefault
- Remove or reorder event.stopPropagation() in submit handlers, or call event.preventDefault() explicitly alongside it
- If you must submit before React handles it, dispatch submit only after hydration/commit completes
- For programmatic resets, use the requestSubmit of the form ref React gives you rather than native submit()
Example fix
// before
const formRef = useRef(null);
<button onClick={() => formRef.current.submit()}>Save</button>;
// after
const formRef = useRef(null);
<button onClick={() => formRef.current.requestSubmit()}>Save</button>; Defensive patterns
Strategy: fallback
Validate before calling
function submitFormSafely(form: HTMLFormElement | null) {
if (form == null) return;
if (typeof form.requestSubmit === 'function') {
form.requestSubmit(); // fires submit event React can preventDefault
} else {
// legacy fallback: dispatch manually so handlers still run
form.dispatchEvent(new Event('submit', {bubbles: true, cancelable: true}));
}
} Type guard
const isSubmitEventUnclaimed = (e: Event) => !e.defaultPrevented;
Try / catch
Wrap the manual submit call: try { form.requestSubmit(); } catch (e) { console.error('Submit blocked', e); } — but note the throw happens in the javascript: URL context, not here, so prevention (requestSubmit) is the real fix. Prevention
- Ban form.submit() in code review; always requestSubmit()
- Never stopPropagation() on submit without also preventDefault()
- Keep submit buttons as type="submit" so the native path goes through React's listener
- Monitor hydration completion before enabling imperative submit buttons
When it happens
Trigger: Calling form.submit() programmatically (bypasses the submit event entirely); calling event.stopPropagation() in your own submit handler before React's root listener runs; submitting a hydrated form before hydration has attached React's listener; CSP logging the blocked javascript: URL instead of showing the throw.
Common situations: Migrating imperative jQuery-style form code to Server Actions; nested libraries (analytics, validation) that stopPropagation on submit; slow hydration where a user submits before React is ready; forms inside third-party modals that call submit() directly.
Related errors
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/3783018f69c8eaad.
Report an issue: GitHub.