Largest Contentful Paint is how long the main content stays blank. Interaction to Next Paint is how long a click looks ignored. Feel free to open Chrome DevTools and look at the real numbers on your own page before trusting an audit. Here are the fixes in the order they usually pay.

LCP: stop making the browser guess

  1. The browser downloads your hero image only after it can see one exists. Adding fetchpriority="high" to that img raises its place in the queue and, measured here, moves LCP more than any library does.
  2. Preload the exact hero URL once on the exact page that uses it, not on every page in the site.
  3. Ship the hero pre-resized, pre-formatted, and hosted on a CDN. A 3 MB JPEG over 4G is where slow websites are born.
  4. Self-host your fonts, or at least preload just the weights you actually use on the LCP text.
<img src="/img/hero.webp" fetchpriority="high" decoding="async" />

INP: stop blocking the main thread

  • Show something in the frame after the tap, and finish the work next. A button that turns to 'Working...' in one painted frame is fine.
  • One heavy listener is worse than ten light ones. Combine work with requestIdleCallback when nothing needs to be instant.
  • Ship less JavaScript. A 300 KB bundle is a queue, not a place to store logic.
btn.addEventListener("click", () => {
  setState("saving");
  requestAnimationFrame(() => doHeavyWork());
});
Nobody visits for the framework. They visit for the page and the speed.

Then re-measure, in Lighthouse and on an actual average phone, because simulators forgive things real 4G does not. Twenty percent improvements in both vitals are normal for this afternoon.