Why does my SSR page lose the click handler after hydration on the edge?

Hey everyone, I’m wiring up a Next.js-style SSR page that’s rendered at the edge, and I’m trying to keep a tiny client bundle, but I’m hitting a weird failure mode where the UI looks right yet clicks sometimes do nothing after navigation.

// server renders this markup
// <button id="buy" data-sku="123">Buy</button>

export function attachBuyHandler() {
  const btn = document.querySelector('#buy');
  if (!btn) return;

  btn.addEventListener('click', (e) => {
    const sku = e.currentTarget.dataset.sku;
    window.dispatchEvent(new CustomEvent('add-to-cart', { detail: { sku } }));
  });
}

// called from a small client entry
attachBuyHandler();

If the server HTML is replaced during hydration or streaming, what’s the most reliable pattern to attach events (without shipping the whole component) so I don’t end up with “dead” buttons?

1 Like