CLS Issues on Salesforce Commerce Cloud
General Guide: See Global CLS Guide for universal concepts and fixes.
SFCC-Specific CLS Causes
1. Dynamic Content Slots
Content slots loading after page render:
<!-- Problem: Slot content causes layout shift -->
<div class="promo-area">
<isslot id="home-promo" context="global" description="Homepage Promo"/>
</div>
2. Einstein Recommendations
Einstein widgets injecting content dynamically:
<!-- Einstein recommendations load async -->
<div class="einstein-recs" data-einstein-type="recently-viewed"></div>
3. Lazy-Loaded Product Images
Images without explicit dimensions:
<!-- Missing dimensions cause shifts -->
<img src="${product.image.url}" loading="lazy" alt="${product.name}">
4. Price Updates via AJAX
Pricing updates based on customer group or promotions:
// Dynamic price loading
$.get('/Pricing-GetPrice', function(data) {
$('.price-display').html(data.formattedPrice); // Causes shift
});
SFCC-Specific Fixes
Fix 1: Reserve Space for Content Slots
Add minimum height or aspect ratio:
/* Reserve space for promo slot */
.promo-area {
min-height: 200px;
}
/* Or use aspect ratio */
.promo-area {
aspect-ratio: 16/9;
width: 100%;
}
<div class="promo-area" style="min-height: 200px;">
<isslot id="home-promo" context="global" description="Homepage Promo"/>
</div>
Fix 2: Pre-render Einstein Placeholders
Use skeleton screens for recommendations:
<div class="einstein-recs" data-einstein-type="recently-viewed">
<!-- Skeleton placeholder -->
<div class="recommendation-skeleton">
<div class="skeleton-item"></div>
<div class="skeleton-item"></div>
<div class="skeleton-item"></div>
<div class="skeleton-item"></div>
</div>
</div>
<style>
.recommendation-skeleton {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
min-height: 300px;
}
.skeleton-item {
background: #f0f0f0;
border-radius: 8px;
animation: pulse 1.5s infinite;
}
</style>
Fix 3: Set Image Dimensions
Always specify width and height in ISML:
<isscript>
var imgWidth = 400;
var imgHeight = 400;
</isscript>
<img
src="${product.getImages('medium')[0].getURL()}?sw=${imgWidth}&sh=${imgHeight}"
width="${imgWidth}"
height="${imgHeight}"
alt="${product.name}"
loading="lazy"
>
For responsive images, use CSS aspect-ratio:
.product-image {
aspect-ratio: 1/1;
width: 100%;
height: auto;
object-fit: cover;
}
Fix 4: Inline Initial Price Display
Render prices server-side initially:
<!-- Server-rendered price (no shift) -->
<span class="price-display" data-product-id="${product.ID}">
<isprint value="${product.price.sales.formatted}"/>
</span>
<script>
// Only update if different from server value
$(document).ready(function() {
$('.price-display').each(function() {
var $el = $(this);
var productId = $el.data('product-id');
var serverPrice = $el.text().trim();
$.get('/Pricing-GetPrice?pid=' + productId, function(data) {
if (data.formattedPrice !== serverPrice) {
// Use transform to avoid layout shift
$el.css('transform', 'scale(1.05)');
$el.html(data.formattedPrice);
setTimeout(function() {
$el.css('transform', 'scale(1)');
}, 200);
}
});
});
});
</script>
Fix 5: Stabilize Header and Navigation
Prevent header shifts from login state:
<header class="site-header" style="height: 80px;">
<div class="user-account">
<isif condition="${customer.authenticated}">
<span class="welcome">Welcome, ${customer.firstName}</span>
<iselse>
<a href="${URLUtils.url('Login-Show')}">Sign In</a>
</isif>
</div>
</header>
<style>
.site-header {
height: 80px; /* Fixed height prevents shift */
}
.user-account {
min-width: 150px; /* Reserve space for content */
}
</style>
Fix 6: Handle Web Fonts Properly
Prevent font-swap layout shifts:
/* Use font-display: optional for critical text */
@font-face {
font-family: 'StorefrontFont';
src: url('/fonts/storefront.woff2') format('woff2');
font-display: optional;
}
/* Fallback with similar metrics */
body {
font-family: 'StorefrontFont', -apple-system, BlinkMacSystemFont, sans-serif;
}
Monitoring CLS on SFCC
// Track CLS with web-vitals
import {onCLS} from 'web-vitals';
onCLS(function(metric) {
dataLayer.push({
'event': 'web_vitals',
'vital_name': 'CLS',
'vital_value': metric.value,
'vital_rating': metric.rating
});
});
CLS Targets
| Rating | CLS Value |
|---|---|
| Good | ≤ 0.1 |
| Needs Improvement | 0.1 - 0.25 |
| Poor | > 0.25 |