# 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.

Source: https://hydration.jscrate.dev/docs/issues/hp5006
Last updated: 2026-09-18

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.

| | |
| --- | --- |
| Code | `HP5006` |
| Name | `double-handler` |
| Default severity | Warning |
| Group | Interaction during hydration |
| What it means | A 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:

```text
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](https://hydration.jscrate.dev/docs/causes/third-party-script) 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:

```tsx title="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](https://hydration.jscrate.dev/docs/ignoring).

## Related

- [HP5001: a click before hydration was lost](https://hydration.jscrate.dev/docs/issues/hp5001)
- [HP4001: the page was modified before React hydrated](https://hydration.jscrate.dev/docs/issues/hp4001)
- [Scripts that change the page before hydration](https://hydration.jscrate.dev/docs/causes/third-party-script)
- [Configuration: checks](https://hydration.jscrate.dev/docs/configuration)
