Tawk.to's history.replaceState override causes URL reset with React Router's setSearchParams

Environment

  • Browser: Chrome
  • React Router (with useSearchParams hook)
  • Tawk.to chat widget

Issue Description

When using setSearchParams from React Router’s useSearchParams() hook, the browser URL gets reset to its initial value from when the page first loaded, rather than updating the current URL’s search parameters.

Steps to Reproduce

  1. Load the application at URL: http://mysite.com/some/initial/url?param1=abc
  2. Navigate to a new route using React Router: http://mysite.com/the/new/url?param1=abc
  3. Call setSearchParams to modify a query parameter
  4. Expected: URL updates to http://mysite.com/the/new/url?param1=xyz (or similar)
  5. Actual: URL reverts to http://mysite.com/some/initial/url?param1=xyz

Root Cause

Tawk.to replaces the native history.replaceState function with a custom implementation that appears to be using a stale/cached value for the URL parameter (i):

window.history.replaceState = function(e, n, i) {
    for (var o = i && "string" == typeof i ? i.indexOf("#max-widget") : -1, 
         a = arguments.length, 
         s = new Array(a > 3 ? a - 3 : 0), 
         c = 3; c < a; c++)
        s[c - 3] = arguments[c];
    
    r.apply(window.history, [e, n, i].concat(s)),
    -1 === o ? t.$store.dispatch("session/sendNavigationEvent", {}, {
        root: !0
    }) : t.isManualHash = !1
}

The third parameter i (the URL) appears to contain a stale value from the initial page load rather than the current URL.

Impact

This breaks standard React Router navigation when using search parameters, as any programmatic update to query strings causes an unexpected navigation back to the initial URL.

I can confirm this bug is happening to me too.
Have you found any workaround?
I tried this and it works but it breaks Clarity tracking

{/* Protect history methods from Tawk.to override (must run before Tawk loads). Unresolved open bug (https://community.tawk.to/t/tawk-tos-history-replacestate-override-causes-url-reset-with-react-routers-setsearchparams/14319) */}
                <scrip
                    dangerouslySetInnerHTML={{
                        __html: `
                            (function() {
                                // Strip #max-widget from URL immediately on page load before Tawk can process it
                                if (window.location.hash && window.location.hash.indexOf('max-widget') !== -1) {
                                    var cleanHash = window.location.hash.replace(/#?max-widget/g, '').replace(/^#$/, '');
                                    var newUrl = window.location.pathname + window.location.search + cleanHash;
                                    history.replaceState(null, '', newUrl);
                                }
                                
                                var originalReplaceState = history.replaceState.bind(history);
                                var originalPushState = history.pushState.bind(history);
                                
                                Object.defineProperty(history, 'replaceState', {
                                    configurable: false,
                                    writable: false,
                                    value: function(state, title, url) {
                                        // Skip Tawk's #max-widget hash manipulations
                                        if (url && typeof url === 'string' && url.indexOf('#max-widget') !== -1) {
                                            return;
                                        }
                                        return originalReplaceState(state, title, url);
                                    }
                                });
                                
                                Object.defineProperty(history, 'pushState', {
                                    configurable: false,
                                    writable: false,
                                    value: function(state, title, url) {
                                        // Skip Tawk's #max-widget hash manipulations
                                        if (url && typeof url === 'string' && url.indexOf('#max-widget') !== -1) {
                                            return;
                                        }
                                        return originalPushState(state, title, url);
                                    }
                                });
                            })();
                        `,
                    }}
                />

(i wrote “scrip” instead of “script” otherwise it wouldn’t let me reply here)