Hydration Proof

Search documentation

Find a page or section

HP5006: An element handles the same event twice

HP5006 (double-handler) means a script listener or inline on* attribute handles an event React also handles on that element, so one click can run twice.

HP5006 (double-handler) means an element has a React event handler and, for the same event, a listener added by a script or an inline on* attribute. One click, submit or key press can then run two actions: a double submit, a double tracking event. Handle the event in one place.

CodeHP5006
Namedouble-handler
Default severityWarning
GroupInteraction during hydration
What it meansA script or an inline handler attribute handles an event on an element that React also handles, so one action can run twice.

What the HP5006 double handler finding means

React handles events at the root of the app. A listener added directly to the element with addEventListener, or an onclick attribute in the HTML, runs in addition to it. hydration-proof records the listeners page scripts add to elements and compares them with the React props of each element during hydration:

React handles "click" on this element, and so does a script listener or an inline onclick attribute. One action can run twice.

The events it tracks are click, submit, input, change, keydown and keyup. The check is part of the props audit (checks.propsAudit, on by default), so it runs on every page without --interactions. It is a warning, and it is not reported on elements marked with suppressHydrationWarning.

Likely causes

  • An analytics or tag manager script that attaches click listeners to buttons and links.
  • An older script, such as a jQuery plugin, that enhances the same form React renders.
  • Server HTML post-processed to add onclick or onsubmit attributes.
  • A script that changes the page before hydration and adds handlers while it does.

How to fix it

Handle the event in one place: remove the inline on* attribute or the script that adds the listener, or remove the React handler.

When the extra listener only tracks the click, call the tracking code from the React handler and stop the script from attaching its own:

components/buy-button.tsx
import { track } from "@/lib/analytics";
 
export function BuyButton({ onBuy }: { onBuy: () => void }) {
  return (
    <button
      type="button"
      onClick={() => {
        track("buy");
        onBuy();
      }}
    >
      Buy
    </button>
  );
}

If the script must stay, check that the two handlers do different things and that neither submits or navigates twice.

When it is intentional

If both handlers are meant to run, add an ignore.issues rule with code: "HP5006", the element's selector and a reason; see ignoring findings.