The page is slow to respond when people tap or click
Interaction to Next Paint measures the delay between a visitor acting — a tap, a click, a keypress — and the screen changing in response.
Why it matters
Interaction to Next Paint measures the delay between a visitor acting — a tap, a click, a keypress — and the screen changing in response. Google's threshold for "poor" is 500ms, and Chrome recorded the observed value at the 75th percentile for your site: a measured share of real interactions were in the poor band. Half a second of nothing happening after a tap is the point at which a person assumes it did not register and taps again. On a checkout or a sign-up button that is a double submission; everywhere else it is the feeling that the site is broken. The cause is almost always JavaScript occupying the main thread when the interaction arrives.
How ShipReady detects it
Core Web Vitals, from the Chrome UX Report. ──────────────────────────────────────────────────────────────────────────── WHY THIS CHECK CAN EXIST AT ALL ──────────────────────────────────────────────────────────────────────────── checks/performance.py ends with a deferral note: the real Core Web Vitals — LCP, INP, CLS — are measured in a rendering browser, so a passive scanner cannot produce them, and that pillar ships only observable proxies ("images have no dimensions, which can cause layout shift", never "your CLS is 0.3"). CrUX changes the premise rather than the constraint. It is Google's public dataset of what REAL Chrome users actually experienced on a site over the trailing 28 days. We are not measuring anything and not estimating anything — we ask Google what it recorded and quote the answer. The scanner still renders nothing. That also makes this the strongest evidence in the whole scanner. Every other rule reasons from what a site COULD do to a visitor; this one reports what it DID do, to real people, in the field. ──────────────────────────────────────────────────────────────────────────── THE THREE OUTCOMES, WHICH MUST NEVER COLLAPSE INTO EACH OTHER ──────────────────────────────────────────────────────────────────────────── 1. DATA, AND IT IS POOR -> a finding per metric in the poor band. 2. DATA, AND IT IS FINE -> silence. We looked, and there is nothing to say. This is a real pass. 3. NO DATA -> ScanSignals.note_incomplete, and NO finding. Outcome 3 is the one that matters. CrUX only publishes an origin once it has enough Chrome traffic to be statistically meaningful and to preserve anonymity, so a new site, a low-traffic site, or an internal tool has no record at all — and that is the COMMON case for this product's users, who are scanning something they just launched. Returning silence for both 2 and 3 would report "Performance: no issues" for a site nobody has ever measured. That is the exact failure the coverage-caveat system exists to prevent, so no-data goes through it and the report says the metrics were not assessed. ──────────────────────────────────────────────────────────────────────────── WHY ONLY THE "POOR" BAND ──────────────────────────────────────────────────────────────────────────── Google defines three bands per metric. This reports only POOR, never "needs improvement" — a deliberate false negative, in the direction this scanner always errs. A site at LCP 3.9s is genuinely not great and will not be told so here; a site at LCP 4.6s is failing real users and will be. Reporting the middle band would roughly triple the finding count for advice that reads as "could be better", which is not what a production-readiness verdict is for. ──────────────────────────────────────────────────────────────────────────── WHAT THIS SENDS, AND WHERE ──────────────────────────────────────────────────────────────────────────── One POST per scan, to Google, containing the scanned ORIGIN and nothing else. No page URLs, no crawl contents, no visitor data — and nothing is sent to the customer's own site, so this check cannot contribute to bot mitigation. Queried by ORIGIN rather than by URL on purpose. Origin-level records are aggregated across the whole site, so they exist for far more sites than per-URL records do, and the answer describes the site rather than whichever page the crawler happened to seed on. REQUIRES `CRUX_API_KEY`. Without one this check is INERT: it returns nothing and records no caveat, because a key we have not deployed is a gap in our product, not a fact about the customer's site. Telling every reader that Core Web Vitals "were not assessed" because of our own configuration would put an unactionable caveat on 100% of reports. It logs `crux_api_key_missing` instead.
Detection is deterministic. ShipReady reports this only when it observes the condition directly, and prefers to miss a real problem over inventing one. Rule version 1.0.0.
How to fix it
This is the prompt ShipReady puts in your report — written to be pasted straight into Cursor, Claude Code, or whichever assistant built the app.
Real Chrome users are waiting past Google's "poor" threshold of 500ms between interacting and seeing anything change. INP is almost always a main-thread problem: when the tap arrives, JavaScript is busy, so nothing can be painted. FIND THE SLOW INTERACTION FIRST. DevTools → Performance, record while you click around, and look for long tasks (over 50ms) next to your interactions. The web-vitals library can also report INP with the exact element attribution in production, which beats guessing from a lab profile. THE THREE USUAL CAUSES. A heavy event handler doing layout-triggering work synchronously — break it up, and read layout values before writing them rather than alternating. A large React re-render on every keystroke or click — memoise, virtualise long lists, and move expensive derived state out of render. Third-party scripts (analytics, chat widgets, tag managers) occupying the main thread — load them with `async`, defer them until after first interaction, or drop the ones nobody reads. YIELD TO THE BROWSER. If a handler must do real work, split it so the browser can paint between chunks: `await scheduler.yield()` where available, otherwise `await new Promise(r => setTimeout(r, 0))`. Paint the response to the user FIRST — disable the button, show the spinner — and do the work after. Field data lags by 28 days, so verify the fix with the web-vitals library in production rather than waiting for this number to move.
Frequently asked questions
- What does "The page is slow to respond when people tap or click" mean?
- Interaction to Next Paint measures the delay between a visitor acting — a tap, a click, a keypress — and the screen changing in response.
- How serious is it?
- ShipReady rates this medium. Fix soon. Meaningfully weakens a defence or degrades how the site works.
- How do I fix it?
- Paste the fix prompt on this page into Cursor, Claude Code or your AI editor. It is the same prompt ShipReady puts in your report.
- Can I check my own site?
- Yes — ShipReady scans up to ten pages of any public site for free and reports this alongside every other check. The free report lists every issue it finds and shows full evidence and a fix prompt for the critical and high-severity ones; medium and low findings are counted and unlock on Pro.
Related checks
- Real visitors wait too long to see your main contentmedium
- The page moves under people while it loadsmedium
- A JavaScript bundle is large enough to make the page feel slowmedium
- An image is large enough to be slowing the page downmedium
- The page is served without compressionmedium
- Build assets are served without cachinglow
Run this check on your site
ShipReady checks this and 193 other things across up to ten pages of your site, with an AI-ready fix for each. Free, no signup.