LCP varies wildly across runs with no code difference

I don’t know at this point. It feels a lot like a Chrome bug of some kind. Do you know if the script is doing something like rehydrating a SSR page? There is a callback for componentDidLoad os something like that while the script was running.

This feels a LOT like a LCP issue I saw recently with AMP where it was racy but the AMP page would load an image, then the layout would shift slightly, moving more of the image into the viewport and then the AMP component would rehydrate the amp-image element, replacing the image in-place with the same DOM node which would trigger a new paint and since slightly more was in the viewport, the new paint would be “larger”.

Looking at the raw video from run #5 in this test and scrubbing the frames, it looks like the text paints with a slightly smaller font (fallback?) and then almost immediately applies the webfont which makes the text area larger but the LCP doesn’t register until quite a bit later after the JS has run.

It feels a lot like what is happening is a similar rehydration problem (just a guess):

  • SSR initially renders with the fallback font, triggering the first LCP candidate
  • Webfont loads, causing the text area to be slightly larger but not triggering a new candidate since it’s not the initial paint for the content (just a layout shift)
  • JS runs and rehydrates the page, causing “new” (identical) elements to render exactly where they were before. Since the area is bigger now and it’s the “initial” paint of this new text node, it counts as a new LCP candidate.

It’s racy because if the initial paint doesn’t use the fallback font then the initial paint will be with the webfont and will be the correct size so the rehydrated paint won’t be larger and won’t count as a new candidate.

For AMP, the solution was to not re-attach the same DOM node when rehydrating and just leave everything in place if it was SSR’d.

1 Like