DEV Community

Daniel Cheong
Daniel Cheong

Posted on

How we cut INP from 480ms to under 200ms on a legacy site (no rewrite)

Most "make it faster" advice assumes you can rebuild. On real small-business sites — a clinic on WordPress, a retailer on a 2018 theme — you usually can't. Here's how we cut Interaction to Next Paint (INP) on a legacy site from ~480ms to under 200ms without a rewrite.

1. Find the slow interactions, don't guess

INP measures the worst interaction, not the average. Pull real-user data first:

import { onINP } from 'web-vitals';
onINP((metric) => {
  // ship to your analytics endpoint
  navigator.sendBeacon('/vitals', JSON.stringify({
    value: metric.value,
    target: metric.attribution?.interactionTarget,
  }));
});
Enter fullscreen mode Exit fullscreen mode

The interactionTarget tells you which element is slow — usually a menu toggle, a filter, or an "add to cart".

2. Break up long tasks

The usual culprit is a single handler doing too much synchronously. Yield to the main thread:

async function handleClick() {
  updateUIImmediately();           // cheap, visible feedback first
  await scheduler.yield?.() ?? new Promise(r => setTimeout(r));
  doExpensiveWork();               // the rest, after paint
}
Enter fullscreen mode Exit fullscreen mode

3. Defer third-party scripts

Chat widgets, pixels and analytics are the silent INP killers. Load them after first interaction or on idle:

<script>
  addEventListener('load', () => requestIdleCallback(() => {
    const s = document.createElement('script');
    s.src = 'https://widget.example.com/chat.js';
    document.body.append(s);
  }));
</script>
Enter fullscreen mode Exit fullscreen mode

4. Measure again

After these three changes, the legacy site moved from a failing INP to passing — no framework migration, no redesign.


If you want this run against your own legacy site, we do exactly this for Singapore SMEs at SGBP — performance and AI work on the site you already have. Happy to take a look — drop us a line.

— Daniel

Top comments (0)