Theme components and Largest Contentful Paint (LCP)

AI-generated summary

The discussion revolves around improving the Largest Contentful Paint (LCP) metric for a Discourse site. piffy has isolated the issue to a banner theme component, which is the largest object painted on the screen for most pages. Despite prioritizing the banner image using fetchpriority="high", the image is still fetched after 2.5s, harming the LCP metric.

Falco suggests that the banner’s size may be the issue, as it is larger than the splash screen, and recommends adding a prefetch meta tag to prioritize the image’s download. However, piffy has tried this solution without success.

piffy believes that the issue is not with the image download time, but rather with the timing of the banner component’s injection into the page. They are considering converting the theme component to a plugin to address this issue.

In the meantime, piffy has implemented a temporary solution by only showing the banner to logged-in users, which has improved the LCP metric.

Other suggestions from the discussion include adjusting the banner’s size or making the splash screen animation bigger. However, piffy would prefer a solution that doesn’t compromise the aesthetic of the banner.

I’m looking to improve the web vitals for my site, specifically LCP. I have been having issues where the time is ~2.7s (should be under 2.5s). From Optimize Largest Contentful Paint  |  Articles  |  web.dev :

Specifically, LCP measures the time from when the user initiates loading the page until the largest image or text block is rendered within the viewport.

I have isolated the problem to a banner theme component I wrote. A banner image I mount as a widget “above-main-container” is the largest object painted on the screen for basically every page

Code
<script type="text/discourse-plugin" version="0.8">
  const h = require("virtual-dom").h; 

  api.createWidget("my-banner", {
    tagName: "div",

    html() {
      return  h("img.banner-center", {src: settings.banner_image, fetchpriority: "high", style: "aspect-ratio: 925 / 359 ; width: 100%"})
    }
  });
</script>

<script type="text/x-handlebars" data-template-name="/connectors/above-main-container/banner">
  {{mount-widget widget="my-banner"}}
</script>

Here’s what I think is happening. The div holding the image is mounted using JS, so a series of prerequisite things need to happen when a discourse page is refreshed before this JS code ends up running. This causes the banner to be fetched after 2.5s, thus harming the LCP metric.

I have attempted to prioritize the banner image using fetchpriority="high", as seen in the code. But I think it does not address the root timing issue here.

Any suggestions on how to prioritize the rendering of this particular theme component? Would it be better to convert it to a plugin? Is there some other way I can inject the banner in as early as possible? Thanks!

Maybe my question is too detailed. Here’s an easier question if someone knows the answer:

When a page is reloaded, will elements from plugins render earlier than theme components?

If your banner is larger than the element we use for the Introducing Discourse Splash - A visual preloader displayed while site assets load you gonna have a bad time for LCP.

If you think that the main issue is the download of the image asset, you can add something like

<link rel="prefetch" href="http://example.com/myimage.webp" />

to your HEAD element in a theme.

I am currently having a bad time :slight_smile: hahaha

I’ve switched over to using a CDN which has not helped. As I mentioned, I think the call to fetch the banner is the thing happening too late, not the fetch time. I will try the prefetch to see if it makes a difference though!

What I did in the meantime until I arrive at a solution is to not show the banner unless you’re logged in. It looks like google is calculating LCP primarily from search traffic, which is typically non-logged in users in my case

LCP will come from all users using Google Chrome, so Android, Windows, MacOS, Linux and Chromebooks.

If you have more pageviews coming from anons on those devices than logged in users, yes, your LCP will reflect performance of those anons.

Good to know.

Do you think I could just work-around this problem by making the splash screen animation bigger?

This is very complicated.

First, I just checked and the splash screen already uses the entire screen (100vh and 100% width).

However, some users won’t get the splash screen if their Discourse boot is fast enough. For those, then the LCP will be set by whatever element is big enough. In your case the banner, so you are bound by it.

I’d try the prefetch meta tag, and ensure it’s a very well optimized asset and that all your assets are delivered via CDN with PoPs close to where your users are.

Does it have to be?

It’s basically a staple at this point, the users love it

And it can’t be adjusted in any way?

Of course I could always shink it or something but I’d prefer a solution that doesn’t compromise on the aesthetic

I just tried out this possible solution and it didn’t seem to make a difference unfortunately. I also replaced the banner image with a trivially small image as a sanity check and it also made no difference to LCP. Thank you for the suggestion though

I don’t know the inner workings of how theme components get injected into the page but my impression is that by the time the banner component is injected, it’s already too late. My next attempt will be to try it as a plugin.

Far from ideal but this has been working

In case anyone has a similar issue