Fix LCP Issues on Kentico (Loading Speed) | OpsBlu Docs

Fix LCP Issues on Kentico (Loading Speed)

Reduce Kentico LCP by enabling output caching, optimizing media library images, and tuning .NET server-side rendering performance.

Largest Contentful Paint (LCP) measures how quickly the main content of a page loads. This guide provides Kentico-specific solutions to achieve LCP under 2.5 seconds.

What is LCP?

LCP measures: The render time of the largest visible content element in the viewport.

Good LCP scores:

  • Good: < 2.5 seconds
  • Needs Improvement: 2.5 - 4.0 seconds
  • Poor: > 4.0 seconds

Common LCP elements:

  • Hero images
  • Header images
  • Video thumbnails
  • Background images
  • Large text blocks

Diagnosing LCP Issues

Using PageSpeed Insights

  1. Visit https://pagespeed.web.dev/
  2. Enter your Kentico site URL
  3. Review the Diagnostics section
  4. Look for:

Using Chrome DevTools

  1. Open DevTools (F12)
  2. Go to Performance tab
  3. Click Record and load page
  4. Look for LCP marker in timeline
  5. Identify which element is LCP
  6. Review loading time

Enable Kentico Debug

<!-- web.config -->
<appSettings>
  <add key="CMSDebugEverything" value="true" />
  <add key="CMSDebugResources" value="true" />
</appSettings>

Access: yoursite.com?debug=1

Kentico-Specific LCP Optimizations

1. Optimize Media Library Images

Kentico Media Library images are often the LCP element.

Enable Automatic Image Resizing

// In your layout or controller
@using CMS.Helpers

@{
    var imageUrl = MediaFileURLProvider.GetMediaFileUrl(mediaFile.FileGUID, mediaFile.FileName);
    // Add resizing parameters
    var optimizedUrl = URLHelper.AddParameterToUrl(imageUrl, "width", "1200");
    optimizedUrl = URLHelper.AddParameterToUrl(optimizedUrl, "height", "600");
}

<img src="@optimizedUrl" alt="Hero image" loading="eager" fetchpriority="high">

Configure Image Processing

<!-- web.config -->
<appSettings>
  <add key="CMSImageQuality" value="85" />
  <add key="CMSMaxImageWidth" value="2000" />
  <add key="CMSMaxImageHeight" value="2000" />
</appSettings>

Use WebP Format

Create a custom transformation:

@using CMS.Helpers
@{
    var supportsWebP = BrowserHelper.IsWebPSupported();
    var imageUrl = MediaFileURLProvider.GetMediaFileUrl(mediaFile.FileGUID, mediaFile.FileName);

    if (supportsWebP)
    {
        imageUrl = URLHelper.AddParameterToUrl(imageUrl, "format", "webp");
    }
}

<img src="@imageUrl" alt="@mediaFile.FileDescription">

Implement Responsive Images

@using CMS.MediaLibrary
@{
    var mediaFile = MediaFileInfoProvider.GetMediaFileInfo(mediaGuid);
    var baseUrl = MediaFileURLProvider.GetMediaFileUrl(mediaFile.FileGUID, mediaFile.FileName);
}

<picture>
    <source
        type="image/webp"
        srcset="@URLHelper.AddParameterToUrl(baseUrl, "width", "400")&format=webp 400w,
                @URLHelper.AddParameterToUrl(baseUrl, "width", "800")&format=webp 800w,
                @URLHelper.AddParameterToUrl(baseUrl, "width", "1200")&format=webp 1200w"
        sizes="(max-width: 768px) 100vw, 1200px">
    <source
        type="image/jpeg"
        srcset="@URLHelper.AddParameterToUrl(baseUrl, "width", "400") 400w,
                @URLHelper.AddParameterToUrl(baseUrl, "width", "800") 800w,
                @URLHelper.AddParameterToUrl(baseUrl, "width", "1200") 1200w"
        sizes="(max-width: 768px) 100vw, 1200px">
    <img src="@URLHelper.AddParameterToUrl(baseUrl, "width", "1200")"
         alt="@mediaFile.FileDescription"
         loading="eager"
         fetchpriority="high">
</picture>

2. Preload LCP Image

In Layout File (_Layout.cshtml)

@using CMS.DocumentEngine
@{
    var currentDoc = DocumentContext.CurrentDocument;
    var heroImage = currentDoc.GetValue("HeroImage");

    if (heroImage != null)
    {
        var imageUrl = MediaFileURLProvider.GetMediaFileUrl((Guid)heroImage, "hero.jpg");
        var optimizedUrl = URLHelper.AddParameterToUrl(imageUrl, "width", "1200");

        <link rel="preload" as="image" href="@optimizedUrl" fetchpriority="high">
    }
}

For Background Images

<link rel="preload"
      as="image"
      href="@heroImageUrl"
      fetchpriority="high">

<style>
  .hero-section {
    background-image: url('@heroImageUrl');
    background-size: cover;
  }
</style>

3. Reduce Server Response Time (TTFB)

Enable Output Caching

For entire pages:

// In your controller
[OutputCache(Duration = 3600, VaryByParam = "none")]
public ActionResult Index()
{
    return View();
}

In web.config:

<system.webServer>
  <caching>
    <profiles>
      <add extension=".html" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange" />
      <add extension=".aspx" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange" />
    </profiles>
  </caching>
</system.webServer>

Enable Kentico Output Caching

  1. Go to SettingsSystemPerformance
  2. Enable Enable partial caching
  3. Enable Cache pages
  4. Set cache minutes appropriately

Optimize Database Queries

using CMS.DocumentEngine;

// BAD: Multiple database calls
var pages = DocumentHelper.GetDocuments()
    .Path("/Products")
    .ToList();

foreach (var page in pages)
{
    var relatedData = page.GetValue("RelatedData"); // Additional query per item
}

// GOOD: Single query with all data
var pages = DocumentHelper.GetDocuments()
    .Path("/Products")
    .Columns("DocumentName", "RelatedData") // Specify needed columns only
    .CombineWithDefaultCulture(false)
    .ToList();

Enable SQL Server Query Store

-- Enable Query Store for performance monitoring
ALTER DATABASE [YourKenticoDB] SET QUERY_STORE = ON;

4. Eliminate Render-Blocking Resources

Defer Non-Critical CSS

<!-- Critical CSS inline -->
<style>
  /* Critical above-the-fold styles */
  .hero { display: block; }
</style>

<!-- Defer non-critical CSS -->
<link rel="preload" href="~/Content/styles.css" as="style"
<noscript><link rel="stylesheet" href="~/Content/styles.css"></noscript>

Bundle and Minify Resources

Using BundleConfig.cs:

using System.Web.Optimization;

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        // JavaScript bundles
        bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
            "~/Scripts/jquery-{version}.js"));

        bundles.Add(new ScriptBundle("~/bundles/app").Include(
            "~/Scripts/app.js",
            "~/Scripts/components.js"));

        // CSS bundles
        bundles.Add(new StyleBundle("~/Content/css").Include(
            "~/Content/site.css",
            "~/Content/components.css"));

        // Enable optimization
        BundleTable.EnableOptimizations = true;
    }
}

In layout:

@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/app")
@Styles.Render("~/Content/css")

Defer JavaScript

<!-- Defer non-critical scripts -->
<script src="~/Scripts/analytics.js" defer></script>
<script src="~/Scripts/app.js" defer></script>

<!-- Async for independent scripts -->
<script src="~/Scripts/social-widgets.js" async></script>
<!DOCTYPE html>
<html>
<head>
    <!-- Only critical resources in head -->
    <link rel="stylesheet" href="~/Content/critical.css">
</head>
<body>
    @RenderBody()

    <!-- Scripts at end of body -->
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/app")
    @RenderSection("scripts", required: false)
</body>
</html>

5. Optimize Web Part Loading (Portal Engine)

Enable Web Part Caching

In web part properties:

  1. Go to Performance tab
  2. Enable Cache minutes
  3. Set appropriate cache duration
  4. Configure Cache dependencies

Use Partial Caching

<%@ OutputCache Duration="3600" VaryByParam="*" %>

<!-- Web part content -->

Lazy Load Below-Fold Web Parts

<script>
  // Lazy load web parts below the fold
  document.addEventListener('DOMContentLoaded', function() {
    var lazyWebParts = document.querySelectorAll('.lazy-webpart');

    if ('IntersectionObserver' in window) {
      var observer = new IntersectionObserver(function(entries) {
        entries.forEach(function(entry) {
          if (entry.isIntersecting) {
            // Load web part content via AJAX
            loadWebPartContent(entry.target);
            observer.unobserve(entry.target);
          }
        });
      });

      lazyWebParts.forEach(function(webpart) {
        observer.observe(webpart);
      });
    }
  });
</script>

6. Optimize Kentico Settings

Disable Unnecessary Features

<!-- web.config -->
<appSettings>
  <!-- Disable features not in use -->
  <add key="CMSEnableCodeEditSiteAdministration" value="false" />
  <add key="CMSEnableOnlineMarketing" value="false" />  <!-- If not using -->
  <add key="CMSEnableTranslations" value="false" />     <!-- If not using -->

  <!-- Optimize ViewState (Portal Engine) -->
  <add key="CMSControlState" value="false" />

  <!-- Enable compression -->
  <add key="CMSEnableOutputFilter" value="true" />
  <add key="CMSEnableOutputCompression" value="true" />
</appSettings>

Configure IIS Compression

<!-- web.config -->
<system.webServer>
  <urlCompression doStaticCompression="true" doDynamicCompression="true" />

  <httpCompression>
    <dynamicTypes>
      <add mimeType="text/*" enabled="true" />
      <add mimeType="application/javascript" enabled="true" />
      <add mimeType="application/json" enabled="true" />
    </dynamicTypes>
    <staticTypes>
      <add mimeType="text/*" enabled="true" />
      <add mimeType="application/javascript" enabled="true" />
      <add mimeType="image/svg+xml" enabled="true" />
    </staticTypes>
  </httpCompression>
</system.webServer>

7. Use CDN for Static Resources

Configure CDN in Kentico

// In App_Start or Global.asax.cs
public class Application : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        // Set CDN URL for media files
        URLHelper.MediaFileUrlFormat = "https://cdn.yoursite.com/media/{0}";
    }
}

CDN for JavaScript/CSS

<environment names="Production">
    <link rel="stylesheet" href="https://cdn.yoursite.com/Content/site.css" />
    <script src="https://cdn.yoursite.com/Scripts/app.js"></script>
</environment>

<environment names="Development,Staging">
    <link rel="stylesheet" href="~/Content/site.css" />
    <script src="~/Scripts/app.js"></script>
</environment>

8. Implement Critical CSS

Create inline critical CSS for above-the-fold content:

<!DOCTYPE html>
<html>
<head>
    <style>
        /* Critical CSS for above-the-fold content */
        body { margin: 0; font-family: Arial, sans-serif; }
        .header { background: #333; color: #fff; padding: 20px; }
        .hero { min-height: 400px; background-size: cover; }
        .hero h1 { font-size: 48px; margin: 0; }
    </style>

    <!-- Preload full stylesheet -->
    <link rel="preload" href="~/Content/site.css" as="style"
    <noscript><link rel="stylesheet" href="~/Content/site.css"></noscript>
</head>
<body>
    <!-- Content -->
</body>
</html>

Testing Your Improvements

1. Before and After Comparison

# Test with PageSpeed Insights
# Record baseline LCP score
# Make optimizations
# Test again and compare

2. Real User Monitoring

Implement RUM with GA4:

<script>
  // Send Core Web Vitals to GA4
  new PerformanceObserver((entryList) => {
    for (const entry of entryList.getEntries()) {
      if (entry.entryType === 'largest-contentful-paint') {
        gtag('event', 'web_vitals', {
          'metric_name': 'LCP',
          'metric_value': Math.round(entry.renderTime || entry.loadTime),
          'metric_rating': entry.renderTime < 2500 ? 'good' : entry.renderTime < 4000 ? 'needs_improvement' : 'poor'
        });
      }
    }
  }).observe({type: 'largest-contentful-paint', buffered: true});
</script>

3. Continuous Monitoring

Set up automated monitoring:

Common LCP Issues and Fixes

Issue: Large Unoptimized Hero Image

Symptoms:

  • LCP > 4 seconds
  • Large image file size (>500KB)
  • Image loads slowly

Solutions:

  1. Resize image to appropriate dimensions
  2. Convert to WebP format
  3. Add preload link
  4. Use fetchpriority="high"
  5. Consider using CDN

Issue: Slow TTFB

Symptoms:

  • Server response >600ms
  • Database queries slow
  • Kentico debug shows slow page processing

Solutions:

  1. Enable output caching
  2. Optimize database queries
  3. Add database indexes
  4. Increase server resources
  5. Enable partial caching

Issue: Render-Blocking Resources

Symptoms:

  • Multiple CSS/JS files blocking render
  • Large CSS files in <head>
  • Synchronous scripts

Solutions:

  1. Inline critical CSS
  2. Defer non-critical CSS
  3. Move scripts to footer
  4. Use async/defer attributes
  5. Bundle and minify resources

Issue: ViewState Size (Portal Engine)

Symptoms:

  • Large HTML payload
  • Hidden __VIEWSTATE field is huge
  • Slow page load

Solutions:

<!-- web.config -->
<appSettings>
  <add key="CMSControlState" value="false" />
</appSettings>

<pages enableViewState="false" />

Or per-page:

<%@ Page EnableViewState="false" %>

Monitoring LCP Over Time

Set Up Alerts

Create alerts for LCP degradation:

Google Analytics 4:

  1. Create custom event for LCP
  2. Set up alert for LCP > threshold
  3. Receive notifications

Application Insights:

var telemetry = new TelemetryClient();
telemetry.TrackMetric("LCP", lcpValue);

Regular Audits

Schedule regular performance audits:

  • Weekly: PageSpeed Insights tests
  • Monthly: Full performance review
  • Quarterly: Comprehensive optimization

Best Practices

  1. Optimize Images: Always resize and compress
  2. Preload LCP Element: Use <link rel="preload">
  3. Enable Caching: Output and browser caching
  4. Minimize TTFB: Server and database optimization
  5. Remove Unused Code: Clean up CSS/JS
  6. Use CDN: Serve static assets from CDN
  7. Monitor Regularly: Track LCP metrics
  8. Test on Real Devices: Mobile and desktop

Additional Resources