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-buttonis 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
transformandopacityfor 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
- Open the todo
- Inspect the element selector
- Reproduce the interaction:
- Use Chrome DevTools → Performance panel
- Record the interaction
- Focus on:
- Event handlers
- Long tasks after input
- Identify blocking code (JS, layout, third-party)
- Apply fixes (split work, defer, simplify)
- 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.