Mastering Core Web Vitals: A Practical Guide to Optimizing INP & LCP
Demystifying Google's latest Core Web Vitals metric — Interaction to Next Paint (INP) — along with concrete techniques for slashing Largest Contentful Paint (LCP).
With Google's official deprecation of First Input Delay (FID) in favor of Interaction to Next Paint (INP), performance engineering entered a new era.
FID only measured how quickly the browser reacted to the very first click on a page. INP evaluates the latency of every single user interaction across the entire lifecycle of a visit.
If your web application freezes for 250ms when a user opens a modal or sorts a table after 10 minutes of browsing, your INP fails.
Here is how we diagnose and conquer INP and LCP in modern web applications.
#1. What Really Causes Bad INP?
INP measures the duration between when a user interacts (taps, clicks, keystrokes) and when the browser paints the updated visual frame.
The 3 phases of an interaction are:
- Input Delay: Time waiting for previous main thread tasks to clear before event listeners execute.
- Processing Time: The time your JavaScript event callback takes to run.
- Presentation Delay: The time required to recalculate styles, layout, and composite pixels on screen.
#The Fix: Yielding to the Main Thread
If an event handler has to execute heavy operations, yield control back to the browser so it can paint intermediate visual feedback:
async function handleFilterChange(filterValue) {
// 1. Immediately provide visual feedback
setPendingUI(true);
// 2. Yield to allow the browser to paint the loading state
await new Promise((resolve) => setTimeout(resolve, 0));
// 3. Perform the computationally heavy task
computeFilteredResults(filterValue);
}
In modern browsers, you can use scheduler.yield():
if ('scheduler' in window && 'yield' in window.scheduler) {
await window.scheduler.yield();
}
#2. Optimizing Largest Contentful Paint (LCP)
LCP measures when the largest visual block (usually hero text, background image, or banner) renders within the viewport.
To hit sub-2.5s LCP:
- Preload hero assets: Use
<link rel="preload">or Next.jspriorityprop on your<Image />. - Eliminate render-blocking CSS: Keep critical CSS inline and defer unused fonts and analytics scripts.
- Serve images in modern WebP / AVIF formats: Compress images to target sub-100KB payloads without visible fidelity loss.
#Summary
Don't wait for your users to complain about sluggish responsiveness. Use the Chrome Web Vitals extension in staging and observe real-user metrics (RUM) in production to ensure smooth 60fps experiences.
Written by Nasikh Mahamood CL
SDE 2 at CAMS. Building high-performance SaaS applications and sharing lessons.