Paint feedback first
Paint a pending state in the next frame; do the heavy work after.
Guillermo Rauch@rauchg“INP is often misunderstood to require painting the full user response within 200ms, when it is really about giving the user feedback that their input is being processed within that time” This article is a must read
Guillermo Rauch@rauchgFirst battle was page loading speed. Next boss is interaction speed. INP, now a Core Web Vital, is tricky to optimize. We built a tool to help:
Guillermo Rauch@rauchgThe @vercel Toolbar INP debugging feature is 🤯 It prevents slowness from creeping in. When an interaction exceeds the acceptable threshold, a toast is shown.
Demo 2 mimics the Vercel Toolbar's slow-interaction toast.
1 · Sort 20,000 invoices
Press Sort in each pane and watch the button.
Work, then paintBad
Paint, then workGood
const nextPaint = () => new Promise((r) => requestAnimationFrame(() => setTimeout(r)));
sortButton.addEventListener("click", async () => {
sortButton.dataset.pending = ""; // 1. the feedback
sortButton.textContent = "Sorting…";
await nextPaint(); // 2. let the browser paint it
render(sortInvoices(invoices)); // 3. the heavy work
delete sortButton.dataset.pending;
});
// React: startTransition(() => setSort("amount")) paints isPending first.
2 · Flag slow interactions
Click the actions; any over 200ms raises a toast.
INP monitorWork first
Worst next paint
–Threshold 200msWhy it matters, pros and cons
Why it matters
INP times input to next paint. A pending state painted in one frame answers the user; the work can finish after.
Pros
- INP drops even when the total work stays the same.
- People see the click landed, so they don't click twice.
- Three lines: set state, yield a frame, then work.
Cons
- The work still blocks after the paint: split long tasks too.
- A pending state on a 30ms task is just a flicker.
- Yielding lets clicks interleave: guard against stale ones.