INP: the Core Web Vital that's quietly failing most Shopify stores
Interaction to Next Paint replaced First Input Delay as a ranking signal in 2024. Most Shopify stores are failing it without knowing why. Here's what INP actually measures and how to fix it.
In March 2024, Google replaced First Input Delay (FID) with Interaction to Next Paint (INP) as the responsiveness Core Web Vital used in ranking. FID measured time to first interaction. INP measures the worst interaction in a page's lifetime.
The change matters because FID was easy to pass. Most sites passed it by default. INP is considerably harder, and a significant number of Shopify stores that were green across all Core Web Vitals are now failing on INP without having changed a single line of code.
What INP actually measures
When a user interacts with a page — clicks a button, taps a size option, opens a nav menu — the browser has to:
- Handle the event
- Execute any JavaScript attached to that event
- Re-render whatever changed as a result
- Paint the update to the screen
INP measures how long this full cycle takes, from the first user interaction to the moment the update is visible. It measures across all interactions on the page and reports the worst one (with a small exception for statistical outliers).
The thresholds:
- Under 200ms — good
- 200–500ms — needs improvement
- Over 500ms — poor
The key difference from FID: FID only measured the time before your event handler started running. INP includes the time your JavaScript actually runs, plus the re-render time. A page that looked responsive by FID's definition can fail INP badly if the event handler is slow or triggers expensive DOM changes.
Why Shopify stores are failing it
Theme JavaScript that blocks the main thread
Most Shopify themes weren't written with INP in mind. A common pattern in Online Store 2.0 themes: event listeners that trigger on interaction and then run synchronous computations — updating cart state, recalculating totals, re-rendering components — all on the main thread.
The main thread can only do one thing at a time. If it's executing JavaScript when a user taps, the tap waits. If your theme's JS takes 300ms to run after a size selection, your INP is at least 300ms.
Shopify's Dawn theme has improved significantly, but themes built on older foundations — or themes with heavy customisation layers added over time — often have main thread blocking patterns that are invisible until you specifically profile for INP.
Third-party app scripts
As covered in the Shopify app performance guide, every JavaScript file that loads on your storefront contributes to main thread competition. During the rendering and initialisation phase, app scripts are running. If a user interacts with the page during this window, their interaction queues until the main thread is free.
The practical effect: interactions in the first 5–10 seconds of a page load are most likely to be slow, because that's when third-party initialisation is happening. For mobile users on slower connections, that window is longer.
Long tasks
Chrome's Performance panel categorises any JavaScript task that takes over 50ms as a "long task." Long tasks are the root cause of INP failures. A long task on the main thread during a user interaction delays the response to that interaction.
Common long task sources on Shopify storefronts:
- Product filtering and sorting operations that re-render large product grids
- Cart drawer open/close animations that trigger layout recalculation
- Search-as-you-type handlers with synchronous filtering of large product arrays
- Variant selection handlers that update multiple page elements simultaneously
Diagnosing INP in your store
The fastest way to understand your INP position: PageSpeed Insights. Enter a URL and look at the field data (real user data from Chrome users, not the lab test). The field data INP is what actually affects your rankings.
If field data shows an INP issue, diagnose in Chrome DevTools:
- Open DevTools → Performance tab
- Click the record button
- Navigate to the page
- Interact with the elements where you suspect slowness (size selectors, add to cart, nav menus, filters)
- Stop recording
In the recording, look for:
- Long tasks (shown in red in the Main thread row)
- The call stack of tasks triggered by your interactions
- The gap between user input timestamp and render timestamp
The Performance Insights panel (in DevTools under the three-dot menu) gives a more structured view of INP-specific data, showing each interaction with its processing and rendering breakdown.
What to actually fix
Defer non-critical JavaScript
If scripts don't need to run immediately on page load, defer or async them. Scripts loaded with defer run after HTML parsing is complete. Scripts with async run as soon as they're downloaded but don't block parsing. Neither should be used for scripts that the page's initial render depends on — but both are correct for analytics, chat widgets, and recommendation engines.
In Shopify theme code, this usually means reviewing how theme app extensions and legacy script tags are loading and ensuring the loading attribute is set appropriately.
Break up long tasks with scheduler
For JavaScript that genuinely needs to run in response to an interaction but takes longer than 50ms, the browser's Scheduler API (scheduler.postTask()) allows you to yield control back to the browser between chunks of work.
// Instead of one long synchronous operation:
function handleVariantChange(variant) {
updatePrice(variant); // 40ms
updateImages(variant); // 60ms
updateInventoryStatus(variant); // 50ms
updateShippingEstimate(variant); // 80ms
}
// Yield between chunks:
async function handleVariantChange(variant) {
updatePrice(variant);
await scheduler.yield();
updateImages(variant);
await scheduler.yield();
updateInventoryStatus(variant);
await scheduler.yield();
updateShippingEstimate(variant);
}
The yield() call returns control to the browser, allowing it to process any pending user interactions before continuing. This doesn't make the total work faster, but it makes the interactions feel faster because the browser can respond to them between chunks.
Optimise variant selection specifically
Variant selection (size, colour, style dropdowns on a PDP) is the interaction that most commonly drives INP failures on Shopify stores. Every selection triggers a cascade: image swap, price update, inventory status check, cart button state update, potentially shipping calculator update.
The optimisation approach:
- Run the visual updates (image swap, price update) synchronously — these are what the user is waiting to see
- Defer the secondary updates (shipping estimate, recommendation refresh) until after the visual update is painted
- Cache variant data client-side so the computation is against in-memory data, not a network request
Audit and remove blocking app scripts
As above: the single highest-leverage action is removing JavaScript that runs on every page and serves no active purpose. A thorough app audit frequently produces 40–60% INP improvement with no application code changes.
The ranking reality
INP is a field data metric. Google uses Chrome User Experience Report (CrUX) data, which represents real user experiences across Chrome users globally. Lab tests (Lighthouse, PageSpeed synthetic tests) run JavaScript differently than real users interacting with a live page, and don't capture INP accurately.
The practical consequence: you can have a 100 Lighthouse score and fail INP in the field. The Lighthouse score is not what Google is measuring for ranking. Field data is.
Check your CrUX data via PageSpeed Insights (the "Discover what your real users are experiencing" section) and the Search Console Core Web Vitals report. Both show real-user data for your URLs. That's the number that matters.