diff --git a/js/recaptcha_v3.js b/js/recaptcha_v3.js
index 4ce9688..ad5c692 100644
--- a/js/recaptcha_v3.js
+++ b/js/recaptcha_v3.js
@@ -6,6 +6,44 @@
 (function ($, Drupal) {
   'use strict';
 
+  function updateTokenElement(element) {
+    // Wait for grecaptcha to be loaded.
+    if (typeof grecaptcha === 'undefined') {
+      var timer = setInterval(function() {
+        if (typeof grecaptcha === 'undefined' || !element) {
+          clearInterval(timer);
+
+          if (element) {
+            doUpdateTokenElement(element);
+          }
+        }
+      }, 500);
+    }
+    else {
+      doUpdateTokenElement(element);
+    }
+  }
+
+  function doUpdateTokenElement(element) {
+    grecaptcha.ready(function () {
+      if (!element) {
+        return;
+      }
+
+      var $element = $(element);
+
+      grecaptcha.execute(
+          $element.data('recaptchaV3SiteKey'),
+          {
+            action: $element.data('recaptchaV3Action')
+          }
+      ).then(function (token) {
+        $element.val(token);
+        $element.trigger('change');
+      });
+    });
+  }
+
   /**
    * Attach recaptcha response token from google with form.
    *
@@ -14,23 +52,25 @@
   Drupal.behaviors.reCaptchaV3 = {
     attach: function (context) {
       $('.recaptcha-v3-token', context).once('recaptcha-v3-token').each(function () {
-        var $token_element = $(this);
-        var timer = setInterval(function() {
-          if (grecaptcha !== undefined) {
-            grecaptcha.ready(function () {
-              grecaptcha.execute(
-                $token_element.data('recaptchaV3SiteKey'),
-                {
-                  action: $token_element.data('recaptchaV3Action')
-                }
-              ).then(function (token) {
-                $token_element.val(token);
-                $token_element.trigger('change');
-              });
-            });
-            clearInterval(timer)
+        var element = this;
+
+        updateTokenElement(element);
+
+        // Update the recaptcha tokens every 90 seconds.
+        // This seems to be the most robust way to always have valid recaptcha
+        // tokens when you don't have control over how the forms are being
+        // submitted. For example normal form submits are synchronous while
+        // Google Recaptcha v3 is asynchonous.
+        // A recaptcha token has a maximum lifetime of 120 seconds.
+        // https://developers.google.com/recaptcha/docs/v3
+        var interval = setInterval(function () {
+          if (!element) {
+            clearInterval(interval);
+          }
+          else {
+            updateTokenElement(element);
           }
-        }, 500);
+        }, 90000);
       });
     }
   };
