DEV Community

Cover image for Fixing INP on Shopify stores: a practical guide for theme developers
Hitesh Singh
Hitesh Singh

Posted on

Fixing INP on Shopify stores: a practical guide for theme developers

Most Shopify performance work still focuses on load speed: compress the hero image, lazy-load below the fold, trim the app list. That matters, but it misses the metric that most often fails on real stores today: Interaction to Next Paint (INP).

INP measures how quickly the page responds visually after a user does something: taps a variant swatch, opens the cart drawer, types in predictive search, clicks "Add to cart". It replaced First Input Delay as a Core Web Vital in March 2024, and it is much harder to pass, because it looks at (nearly) every interaction across the whole visit rather than only the first one.

This post walks through how we find and fix INP problems on Shopify themes. It is aimed at developers who are comfortable in Liquid and JavaScript and want a repeatable process rather than a list of generic tips.

What INP actually measures

An interaction has three phases:

  1. Input delay: the time between the tap and your event handler starting. This is usually caused by something else hogging the main thread (a third-party script, a hydration task, a long-running timer).
  2. Processing time: how long your event handlers take to run.
  3. Presentation delay: the time for the browser to recalculate styles, lay out and paint the next frame after your handlers finish.

The page's INP is roughly its slowest interaction (outliers are trimmed on pages with many interactions). Google's thresholds, measured at the 75th percentile of real users, are:

  • Good: 200 ms or less
  • Needs improvement: 200 to 500 ms
  • Poor: over 500 ms

The important part is "real users". INP is a field metric. Lighthouse in a lab run cannot report it because nobody is clicking anything, so you need field data to know where you stand.

Step 1: Find out which pages and interactions are slow

Start with the data you already have:

  • Shopify admin: the Web performance report in the admin shows INP alongside LCP and CLS for your storefront, with a timeline you can line up against app installs and theme changes.
  • PageSpeed Insights / CrUX: if the store has enough traffic, the "Discover what your real users are experiencing" section gives you INP at the URL and origin level.
  • Search Console: the Core Web Vitals report groups failing URLs by template, which on Shopify usually maps neatly to product, collection, cart and home.

Those tell you where. To learn which interaction and why, add the attribution build of Google's web-vitals library. A small snippet in theme.liquid is enough:

<script type="module">
  import { onINP } from 'https://unpkg.com/web-vitals@4/dist/web-vitals.attribution.js?module';

  onINP(({ value, rating, attribution }) => {
    if (rating === 'good') return;
    const payload = {
      value: Math.round(value),
      target: attribution.interactionTarget,
      type: attribution.interactionType,
      inputDelay: Math.round(attribution.inputDelay),
      processing: Math.round(attribution.processingDuration),
      presentation: Math.round(attribution.presentationDelay),
      page: location.pathname,
    };
    navigator.sendBeacon('https://your-endpoint.example.com/inp', JSON.stringify(payload));
  });
</script>
Enter fullscreen mode Exit fullscreen mode

Send it wherever suits you: a GA4 custom event, a small serverless endpoint, or your existing analytics. After a few days you will have a list like "variant-picker input on product pages, mostly processing time" or "cart-drawer button, mostly input delay". That breakdown tells you which fix to reach for.

For local debugging, Chrome DevTools' Performance panel shows live INP as you click around, and recording an interaction shows you exactly which tasks ran. Turn on CPU throttling (4x or 6x) so a fast laptop behaves more like a mid-range phone.

Step 2: Fix the usual Shopify culprits

In our experience, slow INP on Shopify almost always comes from a handful of patterns.

Variant pickers that do too much at once

A typical variant change handler updates the price, swaps the gallery image, rewrites the URL, re-renders the buy buttons, recalculates stock messaging and fires analytics events, all synchronously. Each piece is small; together they block the next paint.

The fix is to paint the visible change first and defer the rest:

function yieldToMain() {
  if ('scheduler' in window && 'yield' in scheduler) {
    return scheduler.yield();
  }
  return new Promise((resolve) => setTimeout(resolve, 0));
}

async function onVariantChange(variant) {
  // 1. The thing the user is looking at
  updateSelectedSwatch(variant);
  updatePrice(variant);

  // 2. Let the browser paint
  await yieldToMain();

  // 3. Everything else
  updateGallery(variant);
  history.replaceState({}, '', `?variant=${variant.id}`);
  sendAnalytics('variant_change', variant.id);
}
Enter fullscreen mode Exit fullscreen mode

scheduler.yield() is available in Chromium-based browsers; the setTimeout fallback keeps it working elsewhere.

Re-rendering whole sections on every change

Dawn-style themes often call the Section Rendering API on variant change and replace a large chunk of the DOM with the response. Replacing big subtrees forces expensive style and layout work, which shows up as presentation delay. Where you can, swap only the nodes that changed (price, availability, buy button) instead of the whole section, and abort stale requests with an AbortController when the user clicks through variants quickly.

Third-party apps on the main thread

Reviews widgets, upsell popups, chat bubbles, heatmaps and tracking pixels all compete for the main thread. When a long task from one of them is running, your tap waits. This is the main source of input delay.

Practical steps:

  • Audit app embeds in the theme editor and remove anything that isn't earning its keep. Uninstalling an app doesn't always remove its leftover snippets, so check theme.liquid and snippets/ too.
  • Load non-critical widgets on interaction or when they scroll into view, rather than on page load.
  • Move tracking pixels into Shopify's Customer Events (web pixels), which load in a sandboxed environment instead of as scripts injected straight into the theme.
  • Use the Long Animation Frames API (PerformanceObserver with type long-animation-frame) to see which script URLs are behind long frames. It names the scripts responsible, which makes app conversations much more concrete.

Cart drawers and predictive search

The cart drawer often fetches /cart.js, re-renders, runs a free-shipping bar calculation and opens an animation, all in one handler. Open the drawer immediately with a loading state, then fill it in. For predictive search, debounce input (around 150 to 300 ms) and cancel in-flight requests so typing never queues up work.

Large DOMs and expensive CSS

Collection pages with hundreds of product cards, mega menus rendered in full on every page, and heavy CSS selectors all make each frame costlier. Paginate or progressively render long grids, render mega menu content only when it's opened, and consider content-visibility: auto for off-screen sections so the browser can skip their rendering work.

Step 3: Verify and keep it fixed

After shipping a fix, confirm it the same way you found the problem: re-run the interaction in DevTools with throttling, then watch your field data. CrUX and Search Console use a rolling 28-day window, so give it a few weeks before calling it done; your own web-vitals beacon will show movement much sooner.

To stop regressions:

  • Keep the attribution snippet running permanently and alert when the 75th percentile crosses 200 ms on a key template.
  • Add a quick INP check to your process whenever a new app is installed or the theme is updated.
  • Keep a short "performance budget" note in the theme repo: maximum app embeds, no synchronous third-party scripts in <head>, and so on.

Why this is worth the effort

INP failures are felt directly as a store that seems "laggy" on mobile, and those moments happen on the pages closest to purchase: product, cart and checkout entry points. Fixing them also feeds into Core Web Vitals reporting in Search Console, which is part of the wider page experience picture for SEO.

If you'd like a second pair of eyes on a store's Core Web Vitals, or a broader technical SEO and CRO review, our team at Web Consulting Agency does this kind of work for Shopify and custom ecommerce builds.


Written by the team at Web Consulting Agency, a Dublin-based web, SEO and growth consultancy working with ecommerce brands and app companies.

Top comments (0)