PageSpeed Fix · June 2026

Shopify Passive Event Listeners: Fix the PageSpeed Warning (2026)

Shopify passive event listeners warnings happen when mobile touch handlers don't specify passive: true, causing scroll lag on phones and tablets. Run a free Shopify speed test to spot the patterns; Thunder removes competing scripts automatically, then this guide shows the addEventListener fixes for mobile menus, sliders, and swatches.

~12 min read · JavaScript examples and feature detection included

Quick Fix with Thunder

Mobile touch responsiveness depends on clean event handling and minimal script competition. Thunder defers non-critical app scripts and reduces the number of event listeners competing during touch interactions, which often improves scroll performance even before fixing specific passive listener warnings.

Install Thunder to clean up the script environment, then add passive flags to theme event listeners below. For broader mobile optimization, pair this with mobile speed optimization and INP improvement guides.

Install Thunder

Understanding Passive Event Listeners

When you add a touch event listener without the passive flag, the browser has to wait for your JavaScript to finish executing before it can handle scrolling. This creates a blocking situation where every touch event potentially delays smooth scrolling, especially on slower mobile devices.

Passive event listeners promise the browser that your handler won't call preventDefault(). This allows the browser to start handling scroll immediately in parallel with your JavaScript, creating smoother touch interactions and better perceived performance.

Chrome's Lighthouse documentation explains the performance benefit clearly. The warning appears when the audit finds touchstart, touchmove, touchend, or wheel events without explicit passive flags, indicating potential scroll blocking behavior.

Common Shopify Sources of Non-Passive Listeners

Mobile Navigation

Mobile menus, hamburger toggles, and drawer interactions often use touchstart events for better mobile responsiveness.

Product Sliders

Image carousels, product galleries, and related product sliders frequently listen for touch events to enable swipe navigation.

Interactive Elements

Color swatches, size selectors, quantity buttons, and quick-add functionality often enhance touch targets with event listeners.

Step 1: Find Non-Passive Event Listeners

Open Chrome DevTools Console on your store's mobile view. Chrome shows "Added non-passive event listener to a scroll-blocking 'touchstart' event" warnings when pages load. Note the file names and line numbers in these warnings to locate the problematic code.

Search your theme files for addEventListener with touchstart, touchmove, touchend, or wheel events. Common locations include theme.js, section JavaScript files, and any custom slider or interaction scripts.

Also check the Lighthouse audit panel for the specific "Uses passive event listeners" diagnostic, which lists the JavaScript functions and line numbers causing the warning. This is often more precise than Console warnings for tracking down the exact code.

Step 2: Add Passive Flags to Safe Event Listeners

For event listeners that only read touch information (like tracking touch start for analytics or triggering animations), add the passive flag. This is safe when you're not calling preventDefault() to block scrolling:

// Before: blocks scrolling
mobileMenuButton.addEventListener('touchstart', function(e) {
  // Just checking if touch started for mobile-specific behavior
  this.classList.add('touch-active');
});

// After: allows smooth scrolling
mobileMenuButton.addEventListener('touchstart', function(e) {
  this.classList.add('touch-active');
}, {passive: true});

// For multiple options
element.addEventListener('touchmove', handler, {
  passive: true,
  capture: false
});

This pattern works for most mobile menu buttons, product image touch tracking, and analytics touch events that don't need to prevent default browser behavior.

Step 3: Handle preventDefault Cases with Feature Detection

Some interactions legitimately need to block scrolling, like custom swipe gestures or drag interfaces. Use feature detection to add passive flags only when preventDefault() isn't needed:

// Feature detection for passive support
let passiveSupported = false;
try {
  const options = Object.defineProperty({}, 'passive', {
    get: function() {
      passiveSupported = true;
      return false;
    }
  });
  window.addEventListener('test', null, options);
  window.removeEventListener('test', null, options);
} catch (err) {
  passiveSupported = false;
}

// Use appropriately based on need for preventDefault
function addTouchListener(element, handler, needsPreventDefault) {
  if (needsPreventDefault || !passiveSupported) {
    element.addEventListener('touchstart', handler);
  } else {
    element.addEventListener('touchstart', handler, {passive: true});
  }
}

// Example: product image slider that blocks scroll during swipe
addTouchListener(slider, function(e) {
  if (isSwipeGesture(e)) {
    e.preventDefault(); // Block page scroll during swipe
    handleSliderSwipe(e);
  }
}, true); // Needs preventDefault

// Example: menu button that just tracks touch
addTouchListener(menuButton, function(e) {
  showMobileMenu();
}, false); // Safe for passive

This approach gives you passive benefits where possible while preserving necessary preventDefault() behavior for custom interactions.

Step 4: Fix Common Shopify Theme Patterns

Many Shopify themes use similar touch patterns for mobile interactions. Here are the most common fixes:

// Mobile menu drawer toggle
const mobileToggle = document.querySelector('.mobile-nav-toggle');
if (mobileToggle) {
  mobileToggle.addEventListener('touchstart', function() {
    document.body.classList.toggle('mobile-nav-open');
  }, {passive: true});
}

// Product image zoom activation
const zoomTriggers = document.querySelectorAll('.product-image-zoom');
zoomTriggers.forEach(function(trigger) {
  trigger.addEventListener('touchstart', function() {
    this.classList.add('zoom-active');
  }, {passive: true});
});

// Color swatch selection
const colorSwatches = document.querySelectorAll('.color-swatch');
colorSwatches.forEach(function(swatch) {
  swatch.addEventListener('touchstart', function() {
    // Remove active from siblings
    this.parentNode.querySelectorAll('.color-swatch').forEach(function(s) {
      s.classList.remove('active');
    });
    // Add active to this swatch
    this.classList.add('active');
  }, {passive: true});
});

These patterns improve touch responsiveness without preventing scroll, which is exactly what most Shopify mobile interactions need. For broader touch optimization, see the mobile speed optimization guide.

Step 5: Clean Up jQuery and Legacy Event Patterns

Older Shopify themes often use jQuery for mobile interactions, which doesn't support passive flags by default. Update these patterns or add passive flags through modern addEventListener:

// Before: jQuery without passive support
$('.mobile-menu-trigger').on('touchstart', function() {
  $(this).toggleClass('active');
});

// After: Native JavaScript with passive flag
document.querySelectorAll('.mobile-menu-trigger').forEach(function(trigger) {
  trigger.addEventListener('touchstart', function() {
    this.classList.toggle('active');
  }, {passive: true});
});

// Or wrap jQuery in passive addEventListener
const menuTriggers = document.querySelectorAll('.mobile-menu-trigger');
menuTriggers.forEach(function(trigger) {
  trigger.addEventListener('touchstart', function() {
    $(this).toggleClass('active'); // Still use jQuery inside
  }, {passive: true});
});

Modern browsers handle these native patterns more efficiently than jQuery, and you get the passive benefits. For complete jQuery cleanup, see the JavaScript optimization guide and unused code removal guide.

Manual Fix vs Thunder Fix

Event listener issueManual fixThunder fix
Theme touch events block scrollAdd passive: true to safe addEventListener callsReduces competing app event handlers during touch
Mobile menu touchstart warningsReplace jQuery events with native passive listenersCleans up app script competition on mobile
Product slider touch lagUse feature detection for preventDefault vs passiveDefers scripts that might interfere with slider performance
Multiple app touch handlersAudit each addEventListener call individuallyRemoves non-essential app touch tracking automatically

Testing Mobile Touch Performance

After adding passive flags, test on actual mobile devices or Chrome DevTools mobile emulation. Scroll performance should feel smoother, especially during interactions with elements that have touch handlers. Use Chrome's Performance panel to confirm touch events no longer block the main thread.

The Core Web Vitals field data may show improved INP scores if touch blocking was affecting interaction responsiveness. Monitor the Core Web Vitals metrics over time to see the real-user impact.

For comprehensive mobile optimization beyond event listeners, review mobile speed optimization, Total Blocking Time reduction, and third-party script cleanup.

Related Mobile Performance Issues

Passive event listener warnings often appear with other mobile performance problems. Check for forced reflow in touch handlers, non-composited animations on mobile, and excessive main-thread work that compounds touch interaction problems.

For automatic script optimization instead of manual event listener fixes, check Thunder's features or see Thunder pricing for one-click mobile performance improvements.

FAQ

What are passive event listeners on Shopify?

Passive event listeners are touch and scroll handlers that promise not to call preventDefault(), allowing the browser to handle scrolling immediately without waiting for JavaScript. The PageSpeed warning appears when mobile touch events (touchstart, touchmove, wheel) don't specify {passive: true}, causing scroll lag on mobile devices.

Why does Shopify need passive event listeners?

Shopify themes often use touchstart and touchmove events for mobile menus, product sliders, swatches, and drawer interactions. Without the passive flag, the browser must wait for JavaScript to finish executing before handling scroll, creating janky touch interactions especially on slower mobile devices.

When should I NOT use passive event listeners?

Don't use passive: true if your event handler needs to call preventDefault() to block scrolling or zooming. Common examples include custom scroll implementations, drag-and-drop interfaces, or swipe gestures that should override default browser behavior. Use feature detection to avoid errors.

Can Thunder fix passive event listener warnings automatically?

Thunder reduces the number of third-party scripts and app event handlers competing for touch events, which can improve overall touch responsiveness. However, theme-specific event listeners still need manual passive: true flags or code updates to fix the specific PageSpeed warning.

Do passive event listeners affect Core Web Vitals?

Indirectly, yes. Passive event listeners improve scroll responsiveness and touch interaction speed, which can help with INP (Interaction to Next Paint) scores. They don't directly affect LCP or CLS, but better touch handling contributes to overall user experience on mobile devices.

How do I find which scripts need passive event listeners?

Check the Chrome Console for 'Added non-passive event listener' warnings, use Chrome DevTools Lighthouse audit, or search your theme files for addEventListener with touchstart, touchmove, touchend, or wheel events that don't specify {passive: true}.

Optimize scripts first, then fix event listeners

Thunder automatically reduces app script competition during mobile interactions and defers non-essential event handlers. Then add passive flags to theme touch events for smooth mobile scrolling.

Install Thunder