General Guide: See Global CLS Guide for universal concepts and fixes.
What is CLS?
Cumulative Layout Shift measures visual stability. Google recommends CLS under 0.1. Sitefinity generates CLS from widget placeholders that resize when content loads, media library images without explicit dimensions in widget templates, personalization content swaps, and web font loading in Resource Package themes.
Sitefinity-Specific CLS Causes
- Widget placeholder resizing -- Sitefinity widgets render a loading state first, then swap in content at a different height
- Dynamic list widgets -- News List, Blog List, and Events widgets render variable numbers of items with different heights
- Personalization widget swaps -- Sitefinity's personalization engine replaces default widget content with targeted variants after page load
- Media library images without dimensions -- widget templates that render images from the media library often omit
width/heightattributes - Resource Package font loading -- theme fonts loaded via CSS in Resource Packages cause FOUT if
font-displayis not set
Fixes
1. Size Images in Widget Templates
Override widget templates to include dimensions:
<!-- In your Resource Package widget template -->
<!-- Views/News/List.NewsSummary.cshtml -->
@model Telerik.Sitefinity.Frontend.News.Mvc.Models.NewsListViewModel
@foreach (var item in Model.Items)
{
<div class="news-card" style="min-height: 280px; contain: layout;">
@if (item.ThumbnailUrl != null)
{
<div class="card-image" style="aspect-ratio: 16/9; overflow: hidden; background: #f0f0f0;">
<img src="@item.ThumbnailUrl"
width="400" height="225"
alt="@item.Title"
loading="lazy"
style="width: 100%; height: 100%; object-fit: cover;" />
</div>
}
<h3>@item.Title</h3>
<p>@item.Summary</p>
</div>
}
2. Reserve Space for Widget Containers
/* Widget container containment */
.sf-backend-wrp [data-sf-role="widget"] {
contain: layout;
}
/* Specific widget types */
.sfnewsListView { min-height: 400px; contain: layout; }
.sfblogListView { min-height: 400px; contain: layout; }
.sfeventsListView { min-height: 350px; contain: layout; }
.sfimageGalleryView { min-height: 500px; contain: layout; }
.sfcontentBlockView { min-height: 100px; contain: layout; }
/* Navigation widget */
.sfnavigation { min-height: 48px; contain: layout; }
/* Search widget */
.sfsearchBox { min-height: 50px; contain: layout; }
3. Handle Personalization CLS
/* Personalization segment containers */
[data-sf-personalization] {
contain: layout;
min-height: 200px;
}
/* Smooth transition instead of instant swap */
[data-sf-personalization] {
transition: opacity 0.2s ease;
}
[data-sf-personalization].sf-loading {
opacity: 0.7;
}
4. Stabilize List Widget Heights
/* Consistent card heights in list widgets */
.sfnewsListView .sfnewsItem {
min-height: 120px;
contain: layout;
}
/* Fixed-height excerpts */
.sfnewsSummary, .sfblogSummary {
max-height: 4.5em;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
/* Image gallery items */
.sfimageGalleryView .sfimageItem {
aspect-ratio: 1/1;
overflow: hidden;
}
5. Preload Theme Fonts
<!-- In _Layout.cshtml <head> -->
<link rel="preload"
href="@Url.Content("~/ResourcePackages/YourTheme/assets/fonts/brand.woff2")"
as="font" type="font/woff2" crossorigin />
/* In your Resource Package CSS */
@font-face {
font-family: 'BrandFont';
src: url('../fonts/brand.woff2') format('woff2');
font-display: swap;
size-adjust: 103%;
}
Measuring CLS on Sitefinity
- Chrome DevTools Performance tab -- record page load and filter for layout-shift entries, especially during widget initialization
- Test pages with many widgets -- pages with sidebar widgets + content lists + image galleries have the highest CLS risk
- Test personalization on/off -- compare CLS with Sitefinity personalization active vs. disabled
- Mobile testing -- Sitefinity responsive layouts reflow widgets differently on mobile
Analytics Script Impact
- Sitefinity's personalization engine is the biggest CLS risk from the analytics/personalization side
- Sitefinity Insight should load asynchronously with no visible DOM injection
- Cookie consent (GDPR compliance widget) should use
position: fixedoverlay - Avoid analytics scripts that inject survey widgets or NPS popups above existing content