Fix BigCommerce CLS Issues | Blue Frog Docs

Fix BigCommerce CLS Issues

Comprehensive guide to diagnosing and fixing Cumulative Layout Shift issues on BigCommerce stores.

Fix BigCommerce CLS (Cumulative Layout Shift) Issues

Cumulative Layout Shift (CLS) measures visual stability - how much content shifts unexpectedly during page load. Good CLS scores improve user experience and SEO rankings.

Understanding CLS

What is CLS?

CLS measures unexpected layout shifts that occur during the entire lifespan of a page. Every time a visible element changes position from one frame to the next, a layout shift occurs.

Google's CLS Thresholds:

  • Good: Less than 0.1
  • Needs Improvement: 0.1 - 0.25
  • Poor: Greater than 0.25

Common CLS Causes on BigCommerce

  1. Images without dimensions: Images load and push content down
  2. Web fonts (FOUT/FOIT): Font loading causing text reflow
  3. Dynamically injected content: Banners, alerts, or promotional messages appearing
  4. Ads and embeds: Third-party content loading late
  5. Animations: Slideshow transitions, product carousels
  6. Cart drawer/overlay: Fly-out elements affecting layout

Diagnosing CLS Issues on BigCommerce

Step 1: Identify Layout Shifts

Using PageSpeed Insights:

  1. Visit PageSpeed Insights
  2. Enter your BigCommerce store URL
  3. Click Analyze
  4. Scroll to Diagnostics > Avoid large layout shifts
  5. Review which elements are causing shifts

Using Chrome DevTools:

  1. Open Chrome DevTools (F12)
  2. Click More tools > Rendering
  3. Enable Layout Shift Regions
  4. Reload page
  5. Blue highlights indicate layout shifts

Using Web Vitals Chrome Extension:

  1. Install Web Vitals Extension
  2. Visit your store
  3. Click extension icon
  4. View CLS score and click for details

Step 2: Measure Current CLS

Field Data (Real Users):

Lab Data (Simulated):

  • PageSpeed Insights
  • Chrome DevTools Lighthouse
  • WebPageTest

Step 3: Identify Specific Shift Sources

Common shift sources on BigCommerce:

  1. Product images without explicit dimensions
  2. Hero carousels with different image heights
  3. Category banners loading late
  4. Promotional alerts/bars injected dynamically
  5. Third-party apps (reviews, chat widgets)
  6. Web fonts causing text reflow
  7. Cart/search overlays affecting layout

BigCommerce-Specific CLS Fixes

1. Add Image Dimensions to All Images

The most common CLS cause on BigCommerce is images without width and height attributes.

File: templates/components/common/carousel.html

{{#each slides}}
<div class="slide">
  <img src="{{image.data}}"
       alt="{{image.alt}}"
       width="1920"
       height="600"
       {{!-- Explicit dimensions prevent layout shift --}}
       loading="{{#if @first}}eager{{else}}lazy{{/if}}">
</div>
{{/each}}

Key Point: Even with loading="lazy", images MUST have width and height attributes.

Fix Product Grid Images

File: templates/components/products/card.html

<div class="card">
  <figure class="card-figure">
    <img src="{{getImage image 'productgallery' '300w'}}"
         alt="{{image.alt}}"
         width="300"
         height="300"
         loading="lazy"
         {{!-- 1:1 aspect ratio for consistency --}}>
  </figure>
  <div class="card-body">
    <h3 class="card-title">{{name}}</h3>
    <div class="card-price">{{price}}</div>
  </div>
</div>

Product Image Best Practices:

  • Use consistent aspect ratios (1:1 or 4:3)
  • Add explicit width and height
  • Use CSS to make images responsive

CSS for Responsive Images:

.card-figure img {
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1; /* Maintains square aspect ratio */
}

Fix Product Page Main Images

File: templates/components/products/product-view.html

<div class="productView-images">
  {{#each images}}
  <div class="productView-image">
    <img src="{{getImage this 'product' '1280w'}}"
         alt="{{data}}"
         width="1280"
         height="1280"
         {{!-- First image loads eagerly, others lazy --}}
         loading="{{#if @first}}eager{{else}}lazy{{/if}}">
  </div>
  {{/each}}
</div>

2. Reserve Space for Dynamic Content

Promotional Banners

File: templates/components/common/alert.html or promotional banner component

Problem:

<!-- Banner loads late and pushes content down -->
<div class="promo-banner">
  Free shipping on orders over $50!
</div>

Solution:

<!-- Reserve space with min-height -->
<div class="promo-banner" style="min-height: 40px;">
  Free shipping on orders over $50!
</div>

Better CSS Approach:

.promo-banner {
  min-height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
}

Cart Item Count Badge

Problem: Badge appears after page load, shifting layout.

Solution:

<a href="/cart" class="cart-link">
  Cart
  <span class="cart-badge" data-cart-quantity>
    {{#if cart.quantity}}
    {{cart.quantity}}
    {{else}}
    0
    {{/if}}
  </span>
</a>

CSS:

.cart-badge {
  display: inline-block;
  min-width: 20px; /* Reserve minimum space */
  height: 20px;
  line-height: 20px;
  text-align: center;
}

3. Optimize Web Font Loading

Web fonts can cause significant layout shift when they load.

Use font-display: swap

File: assets/scss/settings/global/_typography.scss or CSS

@font-face {
  font-family: 'MyCustomFont';
  src: url('../fonts/MyCustomFont.woff2') format('woff2');
  font-display: swap; /* Show fallback font immediately, swap when loaded */
  font-weight: normal;
  font-style: normal;
}

Font Display Options:

  • swap: Shows fallback text immediately (best for CLS)
  • optional: Uses custom font only if cached (minimal CLS)
  • block: Invisible text flash (causes CLS, avoid)

Preload Critical Fonts

File: templates/layout/base.html

<head>
  {{!-- Preload critical fonts --}}
  <link rel="preload"
        href="{{cdn 'assets/fonts/main-font.woff2'}}"
        as="font"
        type="font/woff2"
        crossorigin>

  {{!-- Use system fonts as fallback --}}
  <style>
    body {
      font-family: 'MyCustomFont', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    }
  </style>
</head>

Match Fallback Font Metrics

Use fallback fonts with similar metrics to reduce shift:

@font-face {
  font-family: 'MyCustomFont';
  src: url('../fonts/MyCustomFont.woff2') format('woff2');
  font-display: swap;
  ascent-override: 95%;    /* Match custom font metrics */
  descent-override: 25%;
  line-gap-override: 0%;
  size-adjust: 100%;
}

BigCommerce carousel components often cause CLS.

File: templates/components/common/carousel.html

<div class="carousel" style="height: 600px;">
  {{!-- Fixed height prevents shift --}}
  {{#each slides}}
  <div class="slide">
    <img src="{{image.data}}"
         alt="{{image.alt}}"
         width="1920"
         height="600"
         loading="{{#if @first}}eager{{else}}lazy{{/if}}">
  </div>
  {{/each}}
</div>

CSS:

.carousel {
  position: relative;
  width: 100%;
  height: 600px; /* Fixed height */
  overflow: hidden;
}

.carousel .slide img {
  width: 100%;
  height: 100%;
  object-fit: cover; /* Maintain aspect ratio */
}

Option 2: Aspect Ratio Box

CSS:

.carousel {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9; /* Or your desired ratio */
  overflow: hidden;
}

.carousel .slide {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

5. Control Third-Party App Layout Shifts

BigCommerce apps can inject content that causes layout shifts.

Reserve Space for Review Widgets

If using a review app (Yotpo, Stamped.io, etc.):

File: templates/pages/product.html

<div class="product-reviews" style="min-height: 200px;">
  {{!-- Reviews will load here --}}
  <div id="review-widget"></div>
</div>

CSS:

.product-reviews {
  min-height: 200px; /* Reserve space for reviews */
  transition: min-height 0.3s ease; /* Smooth transition */
}

.product-reviews.loaded {
  min-height: auto; /* Remove reservation after load */
}

Load Chat Widgets After Page Load

File: Script Manager or templates/layout/base.html

<script>
  // Load chat widget after page load to prevent CLS
  window.addEventListener('load', function() {
    setTimeout(function() {
      // Initialize chat widget
      (function() {
        // Chat widget code here
      })();
    }, 1000); // Delay 1 second after load
  });
</script>

6. Fix Sticky Header Layout Shifts

Sticky headers can cause layout shift if not implemented correctly.

Reserve Space for Sticky Header

CSS:

/* Fixed height header */
.header {
  position: sticky;
  top: 0;
  height: 80px; /* Explicit height */
  z-index: 100;
}

/* Prevent content jump */
body {
  padding-top: 80px; /* Match header height */
}

Alternative: Use CSS Transform

.header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 80px;
  transform: translateZ(0); /* GPU acceleration, smoother */
  will-change: transform;
}

body {
  padding-top: 80px;
}

7. Fix Alert/Notification Bar Shifts

Promotional alert bars that appear/disappear cause CLS.

Always Reserve Space

File: templates/components/common/alert.html

<div class="alert-container" style="min-height: {{#if alert.message}}40px{{else}}0{{/if}};">
  {{#if alert.message}}
  <div class="alert">
    {{alert.message}}
  </div>
  {{/if}}
</div>

Better Approach: Always Render, Use Visibility

<div class="alert-container">
  <div class="alert {{#unless alert.message}}alert--hidden{{/unless}}">
    {{#if alert.message}}
    {{alert.message}}
    {{else}}
    &nbsp; {{!-- Maintain height even when hidden --}}
    {{/if}}
  </div>
</div>

CSS:

.alert-container {
  min-height: 40px;
}

.alert {
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.3s ease;
}

.alert--hidden {
  opacity: 0;
  pointer-events: none;
  /* Still takes up space, no layout shift */
}

8. Optimize Category Page Grid Layout

Use CSS Grid with Fixed Rows

File: templates/pages/category.html

CSS:

.productGrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 20px;
  grid-auto-rows: 400px; /* Fixed row height prevents shifts */
}

.productCard {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.productCard-image {
  flex-shrink: 0;
  height: 250px; /* Fixed image area */
  overflow: hidden;
}

.productCard-body {
  flex-grow: 1;
  padding: 15px;
}

Advanced CLS Optimization

Use content-visibility CSS Property

Modern CSS property to improve rendering performance and reduce CLS:

.product-description,
.product-reviews,
.related-products {
  content-visibility: auto;
  contain-intrinsic-size: 0 500px; /* Estimated height */
}

Benefits:

  • Browser skips rendering off-screen content
  • Reserves estimated space to prevent shift
  • Improves overall page performance

Implement Skeleton Screens

Show placeholder content while actual content loads:

CSS:

.product-skeleton {
  background: linear-gradient(
    90deg,
    #f0f0f0 25%,
    #e0e0e0 50%,
    #f0f0f0 75%
  );
  background-size: 200% 100%;
  animation: loading 1.5s ease-in-out infinite;
}

@keyframes loading {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

.product-card.loading .card-image {
  width: 100%;
  height: 300px; /* Reserve exact space */
}

Use Intersection Observer for Lazy Loading

Implement custom lazy loading with reserved space:

JavaScript:

const imageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      observer.unobserve(img);
    }
  });
});

document.querySelectorAll('img.lazy').forEach(img => {
  imageObserver.observe(img);
});

HTML:

<img class="lazy"
     data-src="actual-image.jpg"
     src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3C/svg%3E"
     width="300"
     height="300"
     alt="Product">

Testing and Validation

Measure CLS Improvements

  1. Baseline Measurement:

    • Run PageSpeed Insights 3 times
    • Average CLS scores
    • Note which elements cause shifts
  2. Apply Fixes

  3. Retest:

    • Clear all caches
    • Wait 10 minutes
    • Run PageSpeed Insights 3 times
    • Average new CLS scores
    • Verify shifts are eliminated

Real User Monitoring

Monitor CLS in Google Search Console:

  1. Core Web Vitals Report
  2. Filter by CLS metric
  3. Review "Poor" URLs
  4. Track improvements over 28-day periods

Debug Specific Shifts

Use Layout Shift Regions in Chrome:

  1. DevTools > Rendering
  2. Enable "Layout Shift Regions"
  3. Reload page and scroll
  4. Blue highlights show where shifts occur
  5. Identify and fix each shift

Common CLS Pitfalls on BigCommerce

❌ Missing Image Dimensions

Problem:

<img src="product.jpg" alt="Product">  <!-- No dimensions! -->

Solution:

<img src="product.jpg" alt="Product" width="300" height="300">

❌ Dynamic Content Without Reserved Space

Problem:

<div class="promo-banner">  <!-- Appears late, causes shift -->
  Sale ends tonight!
</div>

Solution:

<div class="promo-banner" style="min-height: 40px;">
  Sale ends tonight!
</div>

Problem:

<!-- Different image sizes cause shifts between slides -->
<img src="banner1.jpg" height="400">
<img src="banner2.jpg" height="600">  <!-- Different height! -->

Solution:

.carousel {
  height: 600px; /* Fixed height container */
}

.carousel img {
  width: 100%;
  height: 100%;
  object-fit: cover; /* Crop to fit */
}

❌ Web Fonts Without font-display

Problem:

@font-face {
  font-family: 'Custom';
  src: url('font.woff2');
  /* Missing font-display! */
}

Solution:

@font-face {
  font-family: 'Custom';
  src: url('font.woff2');
  font-display: swap;  /* Prevent invisible text */
}

CLS Optimization Checklist

  • Add width and height to all images
  • Use aspect-ratio CSS for responsive images
  • Add font-display: swap to all @font-face rules
  • Preload critical fonts
  • Reserve space for dynamic content (alerts, banners)
  • Fix carousel with consistent heights or aspect ratios
  • Delay third-party widgets (chat, reviews) until after load
  • Use fixed or sticky positioning correctly
  • Test with "Layout Shift Regions" in Chrome DevTools
  • Verify CLS score in PageSpeed Insights
  • Monitor real-user CLS in Google Search Console

Expected Results

Well-optimized BigCommerce stores should achieve:

  • Mobile CLS: 0.05 - 0.10
  • Desktop CLS: 0.02 - 0.08
  • Good CWV Status: 75%+ of URLs passing CLS threshold

Typical improvements:

  • 60-80% CLS reduction from image dimensions
  • 20-40% reduction from font optimization
  • 15-30% reduction from reserved space for dynamic content

When to Seek Help

Contact BigCommerce support or hire a developer if:

  • CLS remains above 0.25 after optimizations
  • Third-party apps cause unavoidable shifts
  • Complex custom theme code needs refactoring
  • Need advanced CSS Grid/Flexbox implementation

Next Steps

Additional Resources

// SYS.FOOTER