Our services

If you can think it, we can make it brainsoft.

Core Web Vitals: what to fix first

Written By: BrainSoft In SEO

A client forwards you a Lighthouse report with three red circles and asks which one matters. Or you inherit a site that scores 40 on mobile and you have a sprint to improve it. The temptation is to open a bundle analyser and start deleting dependencies, which is usually the wrong first move.

Fix in this order: cumulative layout shift first because it is the cheapest to remove, then the largest contentful paint element because it is the most visible to users, then interaction to next paint because it needs the most careful measurement. Work down that list and stop when the numbers are acceptable.

Why CLS comes first

Layout shift is almost always a missing dimension. An image without width and height, a web font that swaps late, an ad slot that injects itself above the fold. The fixes are small, they rarely break anything, and the payoff shows up immediately in field data. If you only have an afternoon, spend it here.

Start by finding what actually moves. Open DevTools, go to the Performance panel, and record a page load with the Layout Shift regions overlay enabled. The shifts flash on screen as you scroll. In the console you can also watch them arrive:

new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput) {
      console.log(entry.value, entry.sources.map(s => s.node));
    }
  }
}).observe({ type: 'layout-shift', buffered: true });

Each logged source points at the node that moved. Nine times out of ten it is an <img> with no intrinsic size, or a banner that renders after a fetch resolves. Reserve the space with an aspect ratio or a fixed min-height on the container, and the shift disappears. Fonts are the second cause: add font-display: swap and preload the one weight you actually use above the fold instead of shipping four.

Then the LCP element

LCP is one image or one block of text. Find out which. In the Performance panel, hover the LCP marker on the timeline and it names the node. That single fact decides everything that follows.

  • If it is a hero image, serve it as WebP or AVIF at the size it renders, add fetchpriority="high", and make sure it is not lazy-loaded.
  • If it is a heading, the font is the bottleneck, not the image. Preload the font file and inline the critical CSS for that block.
  • If it is a background image set in CSS, the browser cannot discover it until the stylesheet parses. Move it to an <img> or preload it.

The most common mistake I see is loading="lazy" on the hero image. It was added for a below-the-fold gallery and got copy-pasted everywhere. Lazy loading the LCP element delays it by a full network round trip. Remove the attribute and re-measure before touching anything else.

Once the element is identified and correctly prioritised, look at the server. Time to first byte sets the floor for LCP, and no amount of front-end tuning beats a slow origin. If TTFB is over 800ms, the fix belongs in caching or infrastructure, and that is work we do regularly through our services.

INP last, because it needs real data

Interaction to next paint measures the worst interaction latency across the whole visit. You cannot judge it from a lab run on a fast laptop. You need field data, either from the Chrome UX Report or from your own RUM, and you need to know which interaction is slow. A dropdown that takes 400ms on a mid-range Android will look instant on your dev machine.

When you do find the culprit, it is usually one of three things: a long task on the main thread from a third-party script, an event handler doing synchronous work it could defer, or a large re-render triggered by a state change that touches too much of the tree. Break the long task with scheduler.yield() or a setTimeout boundary, and move non-urgent work off the critical path:

button.addEventListener('click', async () => {
  renderImmediateFeedback();
  await scheduler.yield();
  await runExpensiveUpdate();
});

Do not start here. INP work is the most invasive and the hardest to verify, and if you begin with it you will spend two weeks refactoring render logic while the layout still jumps on every page load.

Frequently asked questions

Can I improve Core Web Vitals without changing the design?

Almost always, yes. Reserving space for images, dropping unused fonts, removing lazy loading from the hero, and deferring third-party scripts are all implementation changes. A redesign is rarely required to pass the thresholds.

Should I trust Lighthouse or field data?

Use Lighthouse to find specific problems, and field data to decide whether they matter. A lab score of 95 with poor field INP means real users on slower devices are struggling. Google ranks on field data, so that is the number to move.

How long until the fixes show up in Search Console?

The Core Web Vitals report uses a 28-day rolling window, so improvements appear gradually rather than overnight. Expect a few weeks before the URL groups flip from poor to good, even if your own RUM shows the change the same day.


#SEO