Tutorials
Last updated on:
August 26, 2025

How to show the amount of CMS items in Webflow

BRIX Templates Logo
Author
BRIX Templates
How to show the amount of CMS items in Webflow
Article changelog

Aug 26, 2025 - Initial version of the article published

Table of contents

Webflow doesn't provide a native way to display the total count of items in your CMS collections, which can be frustrating when you're trying to show statistics like "324 products available" or "1,247 blog posts published". The good news? There's a simple workaround that automatically counts and displays your collection items using a lightweight JavaScript solution that can even handle collections larger than 100 items.

This guide will show you how to implement a CMS item counter that works with any Webflow collection, giving you the ability to display formatted item counts anywhere on your site, and the best part is that it's 100% free forever with no ongoing costs or third-party dependencies.

Show CMS Number Count In Webflow With Custom JS

Why you need to display CMS item counts in Webflow

Showing collection counts provides valuable context and social proof for your visitors, while also enabling dynamic statistics that update automatically as your content grows. Here's why implementing CMS counting makes sense for different scenarios:

  • E-commerce stores: Display "324 products in stock" or show category counts like "Electronics (189 items)" to help customers understand your inventory scale.
  • Content websites: Show "Over 1,250 articles published" or display post counts per category to demonstrate your content depth and expertise.
  • Portfolio sites: Highlight "247 projects completed" or break down work by type to showcase your experience and specialization.
  • Directory listings: Display "3,847 businesses listed" or show counts per location/category to demonstrate the size of your directory.

How our CMS counter Webflow solution works

The brix-cms counting system we'll implement offers a reliable, performant solution with automatic summing:

  • Automatic detection: Finds all collection lists marked for counting and sums matching names
  • Smart summing: Multiple lists with the same count name are automatically added together
  • Flexible formatting: Display counts with commas (1,234) or abbreviated (1.2K)
  • Performance optimized: Works with hidden collections to minimize page load impact
  • Multiple counters: Count different collection types independently on the same page

How to add the CMS counter script to your Webflow site

Let's implement the counting functionality step by step.

Adding the counter script to your Webflow project

First, we need to add the script that powers the automatic counting and formatting functionality:

1 - Access your Webflow project settings

Go To Site Settings To Add CMS Count Script

2 - Navigate to the Custom Code section

Add CMS Counter Script For Webflow

3 - Locate the "Before </body> tag" field

Inside that code box, please insert the following code:

Webflow CMS Counter Custom Script By Brix Templates
<script>
/**
 * BRIX Templates CMS Counter for Webflow
 * Counts CMS items, displays formatted totals, and automatically sums multiple lists
 * to overcome Webflow's 100-item limit.
 *
 * Features:
 * - Automatic filtered item counting for visible lists.
 * - Global totals for hidden 'phantom' lists.
 * - Supports custom prefixes and suffixes (e.g., "Showing X results").
 * - Flexible number formatting ('1,234', '1.2K', '3.5M', '1.2B').
 * - Robust change detection for filters, search, and dynamic content.
 *
 * @version 2.2.0
 */
(function() {
  'use strict';
 
  // --- Configuration ---
  const COUNT_ATTR = 'brix-cms-count';
  const DISPLAY_ATTR = 'brix-cms-display';
  const FORMAT_ATTR = 'brix-cms-format';
  const PREFIX_ATTR = 'brix-cms-prefix';
  const SUFFIX_ATTR = 'brix-cms-suffix';
  const ENABLE_LOGGING = true; // Set to false to disable console logging for production
  
  // Debounce and interval timings
  const RECOUNT_DEBOUNCE_MS = 80; // Coalesces rapid DOM changes
  const WATCHDOG_INTERVAL_MS = 1000; // Safety-net interval
 
  // --- State ---
  let recountTimer = null;
  let lastKnownCountsSignature = '';
 
  // --- Logging Helpers ---
  function log(message, ...args) {
    if (ENABLE_LOGGING) {
      console.log(`🖥️ BRIX Templates CMS Counter for Webflow: ${message}`, ...args);
    }
  }
  function warn(message, ...args) {
    if (ENABLE_LOGGING) {
      console.warn(`🖥️ BRIX Templates CMS Counter for Webflow: ${message}`, ...args);
    }
  }
 
  // --- Initialization ---
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
 
  function init() {
    log('Initializing v2.2.0...');
    countAndDisplayItems();
    observeDomChanges();
    
    // Start the safety-net watchdog
    setInterval(watchdogCheck, WATCHDOG_INTERVAL_MS);
  }
 
  // --- Core Logic ---
  
  /**
   * Main function to count all items and update all display elements.
   * Automatically determines whether to perform a filtered or global count.
   */
  function countAndDisplayItems() {
    const visibleSums = {};
    const phantomSums = {};
    const hasVisibleSource = {};
 
    const countElements = document.querySelectorAll(`[${COUNT_ATTR}]`);
 
    countElements.forEach((element) => {
      const countName = element.getAttribute(COUNT_ATTR);
      if (!countName) return;
      
      const isListVisible = isElementActuallyVisible(element);
      const items = getDirectDynItems(element);
 
      if (isListVisible) {
        // This is a visible list, so we count only its visible items.
        const visibleItemCount = items.filter(isElementActuallyVisible).length;
        visibleSums[countName] = (visibleSums[countName] || 0) + visibleItemCount;
        hasVisibleSource[countName] = true;
      } else {
        // This is a hidden "phantom" list, so we count all its items for a global total.
        phantomSums[countName] = (phantomSums[countName] || 0) + items.length;
      }
    });
 
    // Resolve final counts: if a visible source exists for a name, use it. Otherwise, fall back to phantom totals.
    const finalCounts = {};
    const allNames = new Set([...Object.keys(visibleSums), ...Object.keys(phantomSums)]);
    allNames.forEach(name => {
      if (hasVisibleSource[name]) {
        finalCounts[name] = visibleSums[name] || 0;
      } else {
        finalCounts[name] = phantomSums[name] || 0;
      }
    });
    
    // Check if counts have actually changed before updating the DOM
    const newSignature = JSON.stringify(finalCounts);
    if (newSignature === lastKnownCountsSignature) {
      log('Counts unchanged, skipping DOM update.');
      return;
    }
    lastKnownCountsSignature = newSignature;
    log('Counts changed, updating display.', finalCounts);
    
    updateDisplayElements(finalCounts);
  }
 
  /**
   * Updates all display elements on the page with the final calculated counts.
   * @param {object} finalCounts - An object mapping count names to their final numbers.
   */
  function updateDisplayElements(finalCounts) {
    const displayElements = document.querySelectorAll(`[${DISPLAY_ATTR}]`);
 
    if (displayElements.length === 0) {
      log('No display elements found on this page.');
      return;
    }
 
    displayElements.forEach((display) => {
      try {
        const displayName = display.getAttribute(DISPLAY_ATTR);
        const count = finalCounts[displayName] ?? 0;
        
        const format = display.getAttribute(FORMAT_ATTR) || 'none';
        const prefix = display.getAttribute(PREFIX_ATTR) || '';
        const suffix = display.getAttribute(SUFFIX_ATTR) || '';
        
        const formattedCount = formatNumber(count, format);
        const finalDisplay = prefix + formattedCount + suffix;
        
        display.textContent = finalDisplay;
 
        // Add title attribute for accessibility (shows full number on hover)
        if ((format === 'short' || format === 'comma') && count >= 1000) {
          display.setAttribute('title', count.toLocaleString('en-US'));
        }
      } catch (err) {
        warn('Error updating a display element:', display, err);
      }
    });
  }
 
  // --- Helper Functions ---
 
  /**
   * Gets only the direct .w-dyn-item children to avoid overcounting nested lists.
   * @param {Element} listElement - The .w-dyn-list element.
   * @returns {Element[]} An array of direct item elements.
   */
  function getDirectDynItems(listElement) {
    const itemsContainer = listElement.querySelector('.w-dyn-items');
    const container = itemsContainer || listElement;
    return Array.from(container.querySelectorAll(':scope > .w-dyn-item'));
  }
 
  /**
   * Robustly checks if an element is visible to the user.
   * Considers display, visibility, hidden attributes, and ancestor properties.
   * @param {Element} el - The element to check.
   * @returns {boolean} True if the element is visible.
   */
  function isElementActuallyVisible(el) {
    if (!(el instanceof Element)) return false;
    // An element is not visible if it has no rendered dimensions
    if (el.getClientRects().length === 0) return false;
    // Check computed styles and attributes up the DOM tree
    let current = el;
    while (current) {
      const style = window.getComputedStyle(current);
      if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
        return false;
      }
      if (current.hasAttribute('hidden') || current.getAttribute('aria-hidden') === 'true') {
        return false;
      }
      current = current.parentElement;
    }
    return true;
  }
  
  /**
   * Formats a number based on the specified format.
   * 'comma' is guaranteed to use commas.
   * 'short' supports K, M, and B for thousands, millions, and billions.
   * @param {number} num - The number to format.
   * @param {string} format - The format type ('comma', 'short', 'none').
   * @returns {string} The formatted number string.
   */
  function formatNumber(num, format) {
    switch (format) {
      case 'comma':
        // Guarantees US-style comma separators, regardless of browser locale.
        return num.toLocaleString('en-US');
      case 'short':
        if (num >= 1_000_000_000) {
          return (num / 1_000_000_000).toFixed(1).replace(/\.0$/, '') + 'B';
        }
        if (num >= 1_000_000) {
          return (num / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M';
        }
        if (num >= 1_000) {
          return (num / 1_000).toFixed(1).replace(/\.0$/, '') + 'K';
        }
        return num.toString();
      case 'none':
      default:
        return num.toString();
    }
  }
 
  // --- Change Detection ---
 
  /**
   * Schedules a debounced recount to avoid excessive calls during rapid DOM changes.
   */
  function scheduleRecount() {
    clearTimeout(recountTimer);
    recountTimer = setTimeout(() => {
      log('Debounced recount triggered.');
      countAndDisplayItems();
    }, RECOUNT_DEBOUNCE_MS);
  }
 
  /**
   * Sets up a MutationObserver to watch for any relevant DOM changes.
   */
  function observeDomChanges() {
    const observer = new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        const target = mutation.target;
        // Check if the mutation target is relevant to our counting logic
        if (target.nodeType === Node.ELEMENT_NODE && 
            (target.closest('.w-dyn-list, [brix-cms-count]') || target.hasAttribute(COUNT_ATTR))
        ) {
          scheduleRecount();
          return; // No need to check other mutations
        }
      }
    });
 
    observer.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['style', 'class', 'hidden', 'aria-hidden'],
    });
  }
  
  /**
   * A safety-net function that runs periodically to catch any changes the observer might miss.
   */
  function watchdogCheck() {
    // This check is lightweight. It recalculates counts in memory but only updates
    // the DOM if the final numbers have actually changed.
    countAndDisplayItems();
  }
 
})();
</script>

When to use site-wide vs. page-specific implementation:

  • Site-wide: Use project-level custom code if you want to display counts across multiple pages, have collection statistics in your footer, or need consistent counting throughout your site.
  • Page-specific: Use Page Settings > Custom Code > Before </body> tag if you only need counting on specific pages like a Homepage or a Blog page.

Setting up your collections for counting

Follow these steps to create an optimized counting setup that handles any collection size.

Step 1: Create your counting collection list(s)

  1. Add a Collection List element to your page
  2. Connect it to the collection you want to count
  3. Add the custom attribute brix-cms-count with a descriptive value like "products", "posts", or "projects"
Prepare Webflow CMS Collection Lists For CMS Counting Script

Step 2: Optimize the collection item template

This process involves creating a "phantom" or "dummy" collection list that exists purely for counting purposes. It is not your main, visible collection list. This lightweight, hidden list ensures accurate counting without affecting your site's design or performance.

Inside your collection item, include only the bare minimum:

  • Keep just a single text element (like the item name)
  • Remove all images, rich text, and complex elements
  • Delete any unnecessary div wrappers
  • Turn OFF "Load images eagerly" in the collection list settings.

This ensures fast loading without impacting performance.

Step 3: Hide the counting collection

Select your entire Collection List wrapper and:

  1. Go to the Style panel
  2. Under Layout, set Display to None
  3. Give it a descriptive class name like "hidden-counter-collection"
Hide Webflow CMS Collection Fields

Step 4: Add the counter display element

Where you want to show the count:

  1. Add a Text element (or Span within existing text)
  2. Add the custom attribute brix-cms-display with the same value as your count (e.g., "products")
  3. Set placeholder text like "0" (this will be replaced by the actual count)

For example, to show "We have [count] products available":

  • Type "We have "
  • Add a span element
  • Add the brix-cms-display="products" attribute to the span
  • Type " products available" after the span
Setup Brix Templates CMS Counting Attribute
Preview CMS Counting Attribute In Webflow Preview

Counting beyond 100 items in Webflow CMS

Webflow collection lists can only display a maximum of 100 items at a time. To count more than 100 items, simply add more than one hidden Collection Lists.

Give them all the same brix-cms-count attribute (e.g., "products"). Then, use Webflow's collection settings to paginate them: the first list shows items 1-100, the second shows 101-200 (by setting "Skip" to 100), the third shows 201-300 (by setting "Skip" to 200), and so on.

The script will automatically add them all up to display the correct total.

Setup CMS Limit Items For CMS Counting

Advanced options for Webflow CMS counting

Additional formatting for Webflow CMS counting

Make your counts more readable with built-in formatting options. To use them, add the brix-cms-format attribute to your display element with one of the following values:

  • comma: Shows numbers with comma separators (e.g., 1,234).
  • short: Abbreviates large numbers (e.g., 1.2K or 3.5M).
  • none: Shows the raw number without formatting (this is the default, so it will be set as it even if you don't use the brix-cms-format attribute).

For example, to display "Total: 50 products", you would add the attribute brix-cms-prefix with a value of 'Total: ' and the attribute brix-cms-suffix with a value of ' products'.

For collections that frequently exceed 1000+ items, adding multiple collection lists can become tedious. In these cases, API-based solutions from services like Jetboost or a custom integration with Webflow's API can provide fully automated counting. These advanced implementations typically require paid subscriptions and/or custom development work. If you need a robust, automated solution for very large collections, our Webflow dev agency can help you with a custom API integration.

Testing and troubleshooting

Before publishing:

  1. Preview in Designer: The counter won't work in the Designer view—this is normal
  2. Publish to staging: Test on your webflow.io domain first
  3. Verify counts: Check that counts match your expected numbers
  4. Test with filters: If using filters, ensure counts update correctly when filtering

Common issues and solutions:

Counter shows "0" or doesn't update:

  • Verify the brix-cms-count and brix-cms-display attributes are spelled correctly
  • Check that values match exactly (e.g., both use "products" not "product")
  • Ensure your collection list is actually rendering items (even if hidden)
  • Confirm the script is properly placed in the Before  tag section
  • Make sure you've published your site after adding the code

Counter shows wrong number with 100+ items:

  • Check that all collection lists with the same name are being counted
  • Verify skip values are set correctly (0, 100, 200, etc.)
  • Ensure no collection lists are accidentally duplicating items

Formatting not working:

  • Check that brix-cms-format is spelled correctly
  • Verify you're using valid format values: "comma", "short", or "none"
  • Make sure format attribute is on the display element, not the count element

Performance issues:

  • Ensure your counting collections have images disabled
  • Keep only the title field in the collection item template
  • Hide all counting collections with display: none

Frequently Asked Questions

How do I display the count of CMS items in Webflow?

Add our counter script to your Webflow project, create collection list(s) with the brix-cms-count attribute, and add text elements with the brix-cms-display attribute where you want counts shown. The script automatically counts items and displays formatted totals, even for collections larger than 100 items.

How to count more than 100 CMS items in Webflow?

Simply add multiple collection lists with different skip values (0, 100, 200, etc.) but the same brix-cms-count value. Our script automatically sums all lists with matching names. For example, two lists both marked as brix-cms-count="products" will have their items added together.

How to format CMS item counts in Webflow (e.g., 1,234 or 1.2K)?

Add the brix-cms-format attribute to your display element with values like "comma" for 1,234 formatting or "short" for 1.2K abbreviations. You can also add prefixes and suffixes using brix-cms-prefix and brix-cms-suffix attributes.

How to count filtered CMS items in Webflow?

The script automatically detects when collection items are filtered (hidden/shown) and updates the count accordingly. This works with Webflow's native filters and third-party solutions like Finsweet CMS Filter.

How to prevent the CMS counter from slowing down a Webflow site?

If configured properly with images disabled and minimal fields displayed, the performance impact is negligible. Always hide counting collections with display: none and avoid loading unnecessary data like images or rich text content.

How to count multiple different CMS collections on one page?

Use unique values for each collection type (like "products", "posts", "team") in both your brix-cms-count and brix-cms-display attributes. Each type will be counted independently.

Conclusion

Implementing CMS item counting in Webflow fills a crucial gap in the platform's native functionality, allowing you to display dynamic statistics that automatically update as your content grows. Our solution handles collections of any size through automatic summing, making it perfect for everything from small portfolios to large e-commerce stores.

The combination of hidden collections, automatic summing, and flexible formatting ensures you get accurate, beautifully formatted counts without impacting user experience. Whether you're showing "47 products" or "3,847 businesses listed", our free Webflow attribute has you covered.

Need help implementing advanced counting features or complex CMS requirements? Our top-notch Webflow development agency specializes in custom CMS solutions including API integrations, complex filtering systems, and enterprise-level functionality that goes beyond Webflow's native capabilities. Reach out to discuss how we can help optimize your Webflow project with professional, scalable solutions.

BRIX Templates Logo
About BRIX Templates

At BRIX Templates we craft beautiful, modern and easy to use Webflow templates & UI Kits.

Explore our Webflow templates
Join the conversation
Join our monthly Webflow email newsletter!

Receive one monthly email newsletter with the best articles, resources, tutorials, and free cloneables from BRIX Templates!

Webflow Newsletter
Thanks for joining our Webflow email newsletter
Oops! Something went wrong while submitting the form.
How to add a back button in Webflow

How to add a back button in Webflow

Add a functional back button to your Webflow 404 or Thank you page with our lightweight script. Works for internal and external navigation.

Jul 23, 2025
How to block Internet Explorer in Webflow

How to block Internet Explorer in Webflow

Detect and block IE users on Webflow with our free script. Prevents broken layouts and poor user experience. Works with all IE versions!

Jul 23, 2025
How to automatically submit current page URL and title in Webflow forms

How to automatically submit current page URL and title in Webflow forms

Add automatic page data tracking to Webflow forms to capture URLs and titles. Works with static pages and CMS collections.

Jul 23, 2025