Slow interactions

This health check detects elements that cause high interaction latency (INP) based on real user data.

It focuses specifically on event listener-driven interactions (e.g. click, tap, input), meaning the delay is directly tied to code executed in response to user input.

Each todo represents a single element where interaction handlers consistently lead to slow responses for a specific device type.

About this check

When this check is triggered

You’ll see this check when:

  • An element’s event listeners contribute significantly to high INP
  • The impact is visible in real user data (p75, p85, p90)
  • There are enough events to validate the pattern
  • The delay occurs during interaction handling (not general JS execution)

What you’ll see

Each todo includes:

  • Element identifier
  • Device type (desktop, mobile, tablet)
  • Interaction latency based on real user sessions

Example:

Investigate slow interaction on mobile
The element .add-to-cart-button is causing high INP on mobile.

Why this matters

INP measures how quickly your site responds to user interactions.

When interaction handlers are slow:

  • The interface feels unresponsive
  • Users may click multiple times
  • Critical flows (checkout, filtering, navigation) degrade

Because this check focuses on event listeners, it directly reflects interaction handling quality, not just background JavaScript.

More context on RUMvision INP and LoAF blogpost.

How to fix it

Most issues come from:

  • Long-running event handlers
  • Heavy synchronous JavaScript triggered by interactions
  • Layout thrashing (forced reflows)
  • Expensive DOM updates
  • Third-party scripts reacting to interactions (e.g. GTM)
  • Animations or style changes that trigger layout recalculation

Possible causes and fixes:

1. Reduce work inside event listeners

Keep interaction handlers as light as possible.

What to check

  • Large synchronous functions inside click/tap handlers
  • Multiple listeners firing for the same interaction

What to do

  • Move non-critical logic out of the handler
  • Debounce or throttle expensive operations
  • Trigger minimal work on interaction, defer the rest

2. Break up long tasks

Even inside handlers, work can be split.

What to do

  • Split heavy logic into smaller chunks
  • Use modern scheduling APIs

Example

// critical work
await scheduler.yield();
// continue non-critical work later

Why this matters

  • Allows the browser to paint sooner
  • Reduces interaction delay without removing functionality

3. Avoid layout thrashing

Certain DOM operations force immediate layout recalculation.

What to check

element.getBoundingClientRect();
element.offsetHeight;

Combined with:

element.style.height = '100px';

Why this matters

  • Forces synchronous layout
  • Blocks the main thread during interaction

What to do

  • Batch reads and writes
  • Avoid mixing layout reads and writes in the same frame
  • Prefer transforms over layout-triggering properties

4. Be careful with animations

Animations can delay interaction feedback.

What to check

  • Animating layout properties (top, left, width, height)
  • CSS transitions triggered on interaction

What to do

  • Use transform and opacity for animations
  • Avoid triggering layout during interaction
  • Keep animations lightweight and non-blocking

5. Reduce DOM work during interaction

Large updates slow down response time.

What to check

  • Re-rendering large components
  • Updating many DOM nodes at once

What to do

  • Limit DOM mutations inside handlers
  • Defer non-visible updates
  • Use virtualization for large lists

6. Audit Google Tag Manager and tracking

Tracking scripts often run on every interaction.

What to check

  • dataLayer.push() triggering many GTM tags
  • Multiple triggers firing on click or input
  • Heavy GTM setups with many listeners

Why this matters

  • GTM runs synchronously in response to events
  • More triggers means more work during interaction
  • Can affect many elements across the site

What to do

  • Reduce the number of GTM triggers and tags
  • Audit which events actually need tracking
  • Move non-critical tracking out of the interaction path

Better approach

When possible, control tracking in your own code:

setTimeout(() => {
dataLayer.push({ event: 'addtocart' });
}, 0);

This allows:

  • Deferring tracking work
  • Avoiding blocking the interaction itself

7. Use passive event listeners where possible

What to check

window.addEventListener('touchstart', handler);

What to do

window.addEventListener('touchstart', handler, { passive: true });

Why this matters

  • Prevents blocking scrolling or input
  • Improves responsiveness on touch devices

Further debugging

How to approach this in RUMvision

  1. Open the todo
  2. Inspect the element selector
  3. Reproduce the interaction:
    • Use Chrome DevTools → Performance panel
    • Record the interaction
  4. Focus on:
    • Event handlers
    • Long tasks after input
  5. Identify blocking code (JS, layout, third-party)
  6. Apply fixes (split work, defer, simplify)
  7. Monitor improvement over the next 7 days

Pro tip

Focus on:

  • High-impact UI elements (buttons, forms, filters)
  • Mobile devices (more sensitive to main-thread blocking)
  • Interactions tied to conversions

Because this check isolates event listener impact, improvements here directly translate to a more responsive interface.