- Xperience by Kentico
Optimizing LCP Core Web Vitals metric in Xperience by Kentico widgets
In this article, I explore the challenges of optimizing the Largest Contentful Paint (LCP) Core Web Vitals metric in Xperience by Kentico widgets, particularly focusing on how to ensure the first image loads with high priority. Follow along as I share practical steps, implementation tips, and key considerations for achieving a faster loading time and a better LCP score.
🔥 The motivation
Lately, I have been working on optimizing the performance of a website built with Xperience by Kentico. My primary focus has been on improving the Core Web Vitals metrics, particularly the Largest Contentful Paint (LCP). In simple terms, LCP measures the time it takes to render the largest visible element during a page load. This is often an image, which was the case for my website as well. The recommended time for a good LCP is under 2.5 seconds, but my test cases were consistently exceeding this threshold.

🤔 Understanding the LCP Optimization Steps
To optimize the LCP metric, Google provides a helpful video that outlines four key steps:
- Ensure the LCP resource starts loading as soon as possible.
- Ensure the LCP element can render as soon as its resource finishes loading.
- Reduce the load time of the LCP resource as much as possible without sacrificing quality.
- Deliver the initial HTML as quickly as possible.
While these recommendations are general and applicable to any website, I'd like to focus on a specific challenge that arises due to the nature of Xperience by Kentico (XbyK) widgets: ensuring the LCP resource starts loading as soon as possible.
In this article, I'll focus on the first step: making sure the LCP resource, such as an image, begins loading with high priority immediately. I'll demonstrate the problem and its solution using a simplified example.
⚔️ The Challenge with Resource Loading in Xperience by Kentico
In XbyK, imagine pages with a static layout that includes a main navigation bar and a footer. Between these two elements, there is an Editable Area designed to hold page content in the form of sections and widgets, which are fully managed by site editors.

The LCP element for each page is likely to be an image—the first image rendered within any widget placed on the page. To enhance performance, when implementing widgets, I set images to lazy-load, as demonstrated in the code snippet below:
<img src="<image-url>" loading="lazy" width="<image-width>" height="<image-height>" alt="<image-alt>" />
The Problem
The question is: How do I ensure that the first image on a page, regardless of which widget it is placed in, loads as soon as possible with high priority instead of being lazy-loaded, as demonstrated in the code snippet below?
<img src="<image-url>" loading="eager" fetchpriority="high" width="<image-width>" height="<image-height>" alt="<image-alt>" />
This can be a tricky problem because widgets in XbyK are standalone components that are unaware of their surroundings or the order in which they are rendered on the page.
📊 Measuring the LCP Score
Before diving into the solution, let’s see how we can measure the LCP score.
- Open Chrome DevTools.
- Go to the Performance tab.
- Open the Capture Settings and configure the browser to simulate a less performant device with a slower internet connection. My testing settings are:
- CPU: 6x slowdown
- Network: Slow 4G
- Click the Record and Reload button to capture the performance data.

In the initial unoptimized setup, the LCP resource is the first image on the page (an image of a cake, in this example). Since the image is lazy-loaded, it is assigned a low loading priority, resulting in a poor LCP score of 4.19 seconds. Our goal is to improve this score by prioritizing the loading of the first image and avoiding lazy-loading.

💡 The Solution: Prioritizing LCP Resource Loading within Widgets
Our goal is to identify the first image rendered within any widget on the page and ensure that it is loaded immediately with high priority.
High-Level Approach
To achieve this, we can use the following approach:
- Create a flag within the context of each request to track whether the first image has been identified.
- When rendering an image, check this flag:
- If the first image has not yet been identified, render the
<img>
tag with attributes that ensure immediate, high-priority loading. Then, set the flag to indicate that the first image has been found. - For all subsequent images, render the
<img>
tag with lazy loading.
- If the first image has not yet been identified, render the
Implementation
We set up the flag in the page controller using the HttpContext.Items
dictionary.
using Kentico.Content.Web.Mvc.Routing;
using Microsoft.AspNetCore.Mvc;
...
[assembly: RegisterWebPageRoute(...)]
namespace Project.Controllers;
public class PageController(...) : Controller
{
private const string LcpElementKey = "LcpElementFound";
public async Task<IActionResult> Index()
{
var model = ...; // Get the page model
HttpContext.Items[LcpElementKey] = false; // Set the flag
return View(model);
}
}
Then, in the view where images are rendered, we implement a check for this flag.
@using Project.Models
@model ImageReusableContentViewModel
@{
const string LcpElementKey = "LcpElementFound";
bool isLcpElement = false;
if (Context.Items.TryGetValue(LcpElementKey, out object value) && value is bool lcpFound)
{
isLcpElement = !lcpFound;
if (isLcpElement)
{
Context.Items[LcpElementKey] = true;
}
}
}
<img src="@Model.Url" loading="@(isLcpElement ? "eager" : "lazy")" @(isLcpElement ? "fetchpriority=high" : "") width="@Model.Width" height="@Model.Height" alt="@Model.Alt" />
Testing the Results
After implementing this solution, the first image on the page is loaded immediately, while subsequent images are lazy-loaded. When we test the page again, the LCP score improves significantly. In our case, the LCP dropped to 2.16 seconds, which is within the recommended range.

🧐 Additional Considerations
Here are some additional steps you can take to further optimize the LCP and other Web Vitals metrics in Xperience by Kentico:
- Script Handling: Xperience by Kentico requires you to include the
<page-builder-scripts>
tag in your site layout to ensure proper functionality of forms and other features. Unfortunately, that tag resolves into<script>
tags Kentico’s custom JavaScript. These are not marked asasync
ordefer
, which makes them render-blocking. To minimize their impact, place these script tags as close as possible to the closing</body>
tag. In the screenshots, you may have noticed the jQuery library being linked. Previously, it was included as part of the<page-builder-scripts>
; however, from version29.6.0
, linking this library is optional. - Image Compression: To reduce the load time of the LCP resource, consider compressing your images using modern formats like AVIF or WebP. Also, serve images in appropriate sizes, taking into account the target device's screen resolution and pixel density. I've written another article on this topic that you may find helpful. Also, in version
30.0.0
, Kentico introduced a built-in functionality to optimize images during their upload to the CMS. - Initial HTML Delivery: The speed at which the initial HTML is delivered (TTFB metric – Time to First Byte) depends heavily on your server-side implementation and the performance of the underlying infrastructure.
⭐ Conclusion
Optimizing LCP can be challenging, especially in environments like Xperience by Kentico where content is dynamically managed. By prioritizing the loading of the first visible image and following the additional tips provided, you can significantly improve your website's LCP score and, ultimately, its overall user experience.
Happy optimizing!
About the author
Milan Lund is a Freelance Web Developer with Kentico Expertise. He specializes in building and maintaining websites in Xperience by Kentico. Milan writes articles based on his project experiences to assist both his future self and other developers.
Find out more