A step-by-step guide for submitting a React form when the submit button is disabled or blocked.
If the submit button has a disabled attribute, remove it:
document.querySelector('[data-test="form-submit"]').removeAttribute('disabled');Note: On React apps, this alone won't trigger submission — React manages its own internal state separately from the DOM.
Validate all form fields to find any empty or invalid ones:
document.querySelectorAll('form input, form textarea').forEach(el => {
console.log(el.name, el.value, el.checkValidity(), el.validationMessage);
});Look for any field with an empty value — a common culprit is g-recaptcha-response.
Check what's available on the grecaptcha global:
console.log(Object.keys(grecaptcha));- If you see
ready,execute, etc. → it's reCAPTCHA v2/v3 - If you see
enterprise→ it's reCAPTCHA Enterprise (continue below)
For Enterprise, check available methods:
console.log(Object.keys(grecaptcha.enterprise));Find the reCAPTCHA site key from the page:
const siteKey = document.querySelector('[data-sitekey]')?.dataset.sitekey
|| document.querySelector('iframe[src*="recaptcha"]')?.src.match(/[?&]k=([^&]+)/)?.[1];
console.log('site key:', siteKey);Use the site key to generate a valid token:
grecaptcha.enterprise.ready(async () => {
const token = await grecaptcha.enterprise.execute('YOUR_SITE_KEY', { action: 'submit' });
console.log('token:', token);
});React forms don't respond to native DOM events, so you need to access React's internal fiber tree to call the onSubmit handler directly. Inject the token and trigger submission together:
grecaptcha.enterprise.ready(async () => {
const siteKey = 'YOUR_SITE_KEY';
const token = await grecaptcha.enterprise.execute(siteKey, { action: 'submit' });
// Inject the reCAPTCHA token into the hidden field
document.querySelector('[name="g-recaptcha-response"]').value = token;
// Trigger React's onSubmit handler via the fiber tree
const form = document.querySelector('form');
const fiberKey = Object.keys(form).find(k =>
k.startsWith('__reactFiber') || k.startsWith('__reactInternalInstance')
);
let node = form[fiberKey];
while (node) {
const props = node.memoizedProps || node.pendingProps;
if (props && props.onSubmit) {
props.onSubmit({
preventDefault: () => {},
stopPropagation: () => {},
nativeEvent: new Event('submit'),
target: form,
currentTarget: form,
bubbles: true,
isTrusted: true
});
break;
}
node = node.return;
}
});Open DevTools → Network tab before running the script. After running, look for:
- A
GETrequest torecaptcha_en.jsor similar → reCAPTCHA verified ✅ - A
POST/fetchrequest to the form endpoint → form submitted ✅
Both returning 200 means your form was submitted successfully.
- reCAPTCHA tokens expire after ~2 minutes. Run the full Step 6 script in one go — don't generate the token separately and paste it later.
- This technique works because React stores its event handlers in a virtual fiber tree, not as standard DOM event listeners.
- If the server returns an error despite a 200 network status, the issue is server-side (e.g., session expired, missing auth header).