Tutorials
Last updated on:
Feb 19, 2025

How to add animated number counters to your Webflow site

BRIX Templates Logo
Author
BRIX Templates
How to add animated number counters to your Webflow site
Table of contents

Want to add animated numbers to your Webflow site? While Webflow offers extensive design tools, number animations aren't included out of the box. This guide shows you how to implement the BRIX Templates Counter attribute, a straightforward solution that brings dynamic number animations to your site with no code.

Whether you're showcasing statistics, tracking goals, or adding dynamic elements to your site, you'll learn how to implement and customize number animations effectively.

Why use animated counters in your Webflow projects

Animated counters serve multiple practical purposes in modern web design:

  • Performance metrics: Dynamically present key statistics, conversion rates, and business achievements in a way that captures attention and emphasizes growth. Perfect for showcasing year-over-year improvements or quarterly results.
  • Social proof: Display growing follower counts, customer numbers, and review scores with animations that reinforce the momentum of your success. This builds immediate credibility with new visitors.
  • Engagement: Create visual interest through animated reveals of important numbers, keeping visitors engaged and focused on key metrics. These animations naturally draw the eye and improve information retention.
  • Goal tracking: Show fundraising progress, participation rates, and milestone achievements with dynamic counters that create a sense of movement and progress. This helps motivate further participation and involvement.
Example of animated number counter on Webflow

By animating these values instead of displaying them statically, you create a more engaging experience that naturally draws attention to important metrics.

How to add the counter script to your Webflow site

How to add the core script to your project

  1. Access your Webflow project settings
  2. Navigate to Custom Code
  3. Find the "Before </body>" section
Add number counter animation script for Webflow

Once you are in there, insert the BRIX Templates Counter attribute script below

<script>
/*!
 * BRIX Templates Number Counter Attribute for Webflow
 * ----------------------------------------------------------------------------
 * Creates animated number counters using a simple attribute system. Perfect for
 * statistics, metrics, and dynamic number displays.
 * 
 * Two Ways to Use:
 * 1. Simple Version:
 *    [brix-counter="start:0;end:100"]
 * 
 * 2. Advanced Version:
 *    [brix-counter="start:0;end:200;duration:3;format:comma;trigger:scroll"]
 * 
 * Supported Options:
 *    start: Where to begin counting (required)
 *    end: Where to stop counting (required)
 *    duration: How long to animate (in seconds)
 *    step: Count by specific numbers (e.g., by 5s or 10s)
 *    format: How to show thousands ("comma", "period", "none")
 *    prefix: Add text before the number ("$", "€", etc.)
 *    suffix: Add text after the number ("+", "k", etc.)
 *    trigger: When to start ("load", "scroll", "click")
 *    delay: Wait time before starting
 *    easing: Animation style ("linear", "easeIn", "easeOut", "easeInOut")
 *    replay: Animation behavior ("once", "multiple")
 *    direction: Count direction ("normal", "inverse")
 * 
 * Version: 1.0.1
 * Author: BRIX Templates
 */

(function() {
  'use strict';

  // -------------------------------------------------------------
  // 1) Easing functions
  // -------------------------------------------------------------
  const EASING = {
    linear:   t => t,
    easeIn:   t => t * t,
    easeOut:  t => t * (2 - t),
    easeInOut(t) {
      return (t < 0.5) ? 2*t*t : -1 + (4 - 2*t) * t;
    },
  };

  function getEasingFunction(name) {
    return EASING[name] || EASING.linear;
  }

  // -------------------------------------------------------------
  // 2) On DOM Ready
  // -------------------------------------------------------------
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initBrixCounters);
  } else {
    initBrixCounters();
  }

  // -------------------------------------------------------------
  // 3) Main function: find all [brix-counter] elements
  // -------------------------------------------------------------
  function initBrixCounters() {
    const counters = document.querySelectorAll('[brix-counter]');
    if (!counters.length) return;

    counters.forEach((el) => {
      setupCounter(el);
    });
  }

  // -------------------------------------------------------------
  // 4) Parse attribute => set up each counter
  // -------------------------------------------------------------
  function setupCounter(el) {
    const rawAttr = el.getAttribute('brix-counter') || '';
    const config  = parseCounterParams(rawAttr);

    // read config with defaults
    let {
      start, end, duration, step, format, prefix, suffix,
      trigger, delay, easing, replay, direction
    } = config;

    start    = parseFloat(start)    || 0;
    end      = parseFloat(end)      || 100;
    duration = parseFloat(duration) || 2;
    step     = parseFloat(step)     || 1;
    format   = format   || 'none';
    prefix   = prefix   || '';
    suffix   = suffix   || '';
    trigger  = trigger  || 'load';
    delay    = parseFloat(delay) || 0;
    easing   = easing   || 'linear';
    replay   = replay   || 'once';
    direction= direction|| 'normal';

    // if direction=inverse => swap start & end
    let realStart = start;
    let realEnd   = end;
    if (direction === 'inverse') {
      realStart = end;
      realEnd   = start;
    }

    // set initial text as realStart
    el.textContent = formatNumber(realStart, step, format, prefix, suffix, realStart, realEnd);

    // define an animate function
    let hasRun = false;

    function animateCounter() {
      // skip if replay=once and has run already
      if (hasRun && replay === 'once') return;
      hasRun = true;

      const startTime  = performance.now();
      const durationMs = duration * 1000;
      const delayMs    = delay    * 1000;
      const easeFn     = getEasingFunction(easing);

      // after the delay, do an anim loop
      // show the start number during the delay
      setTimeout(() => {
        const animStart = performance.now(); // the moment we actually start anim

        function frame(now) {
          const elapsed = now - animStart;
          let progress  = elapsed / durationMs;
          if (progress < 0) progress = 0;
          if (progress > 1) progress = 1;

          const eased   = easeFn(progress);
          const rawVal  = realStart + (realEnd - realStart) * eased;
          const stepped = stepRound(rawVal, step, realStart, realEnd);

          el.textContent = formatNumber(stepped, step, format, prefix, suffix, realStart, realEnd);

          if (progress < 1) {
            requestAnimationFrame(frame);
          }
        }
        requestAnimationFrame(frame);
      }, delayMs);
    }

    // attach triggers
    switch(trigger) {
      case 'load':
        animateCounter();
        break;
      case 'scroll':
        attachScrollTrigger(el, animateCounter, replay);
        break;
      case 'click':
        el.addEventListener('click', animateCounter);
        break;
      default:
        animateCounter();
        break;
    }
  }

  // -------------------------------------------------------------
  // 5) parse "key:value; key:value; ..."
  // -------------------------------------------------------------
  function parseCounterParams(str) {
    const parts = str.split(';');
    const conf = {};
    parts.forEach(part => {
      const trimmed = part.trim();
      if (!trimmed) return;
      const [k, v] = trimmed.split(':');
      if (!k || v === undefined) return;
      conf[k.trim()] = v.trim();
    });
    return conf;
  }

  // -------------------------------------------------------------
  // 6) Round rawVal to nearest multiple of step
  // -------------------------------------------------------------
  function stepRound(rawVal, step, minVal, maxVal) {
    if (step <= 1) {
      return rawVal; 
    }
    const ratio   = rawVal / step;
    const rounded = Math.round(ratio) * step;
    if (minVal < maxVal) {
      return Math.max(minVal, Math.min(maxVal, rounded));
    } else {
      // if inverse scenario
      return Math.min(minVal, Math.max(maxVal, rounded));
    }
  }

  // -------------------------------------------------------------
  // 7) formatNumber
  // -------------------------------------------------------------
  function formatNumber(val, step, format, prefix, suffix, minVal, maxVal) {
    const n = Math.floor(val);
    let out = n.toString();
    if (format === 'comma') {
      out = out.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    } else if (format === 'period') {
      out = out.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
    }
    return prefix + out + suffix;
  }

  // -------------------------------------------------------------
  // 8) IntersectionObserver for scroll triggers
  //    if replay=multiple => keep observing
  // -------------------------------------------------------------
  function attachScrollTrigger(el, callback, replayMode) {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          callback();
          if (replayMode === 'once') {
            observer.unobserve(el);
          }
        }
      });
    }, { threshold: 0.2 });
    observer.observe(el);
  }

})();
</script>

If you prefer to use the counter on specific pages only, you can alternatively add it through:

  1. Open the Webflow Designer
  2. Navigate to Pages
  3. Select Page Settings
  4. Find the "Before </body>" section
  5. Insert the same script mentioned above
Add number counting functionality to Webflow

How to configure counter animation in Webflow

After adding the script, you can set up individual counter elements:

  1. In the Webflow Designer, select the text element that contains your static number — This could be a heading, paragraph, or any text element where you want the number to animate
  2. Open the element's Settings panel (gear icon)
  3. Locate the Custom Attributes section
  4. Add a new attribute:
    • Name: brix-counter
    • Value: Your configuration string (explained below 👇🏻)
Setup pop counter number in Webflow

How to customize your counter animations on Webflow

The brix-counter Webflow attribute accepts various parameters in a single string, separated by semicolons. Here are the key configuration options:

brix-counter="start:0;end:200;duration:3;step:10;format:comma;prefix:$;suffix:+;trigger:scroll;delay:2;easing:easeInOut;replay:multiple;direction:normal"

Essential parameters

  • start: Initial number (e.g., "0")
  • end: Final number (e.g., "500")
  • duration: Animation length in seconds (e.g., "3")
  • step: Increment size (e.g., "10" increases by multiples of 10)

Formatting options

  • format: Choose thousand separators
    • "comma" for 1,000
    • "period" for 1.000
    • "none" for 1000
  • prefix: Add text before the number (e.g., "$")
  • suffix: Add text after the number (e.g., "+")

Animation control

  • trigger: Choose how the animation starts
    • "load": Begins when page loads
    • "scroll": Activates when element enters viewport
    • "click": Starts when clicked
  • delay: Wait time before animation starts (in seconds)
  • easing: Animation style
    • "linear": Constant speed
    • "easeIn": Starts slow, ends fast
    • "easeOut": Starts fast, ends slow
    • "easeInOut": Smooth acceleration and deceleration
  • replay: Animation behavior
    • "once": Plays one time only
    • "multiple": Can repeat
  • direction: Count direction
    • "normal": Counts up
    • "inverse": Counts down

How to use the number counter Webflow generator tool

To simplify the configuration process, you can use our Counter Generator tool:

  1. Configure your desired number count settings:
    • Set numeric values (start, end, duration)
    • Choose formatting options
    • Select animation preferences
  2. Click "Preview" to test your configuration
  3. Click "Generate" to get the complete attribute code

After generating your counter configuration, paste the complete code into the 'value' field of your brix-counter attribute in the Webflow element settings.

Add attribute for number counter functionality on your Webflow site

You can follow these same steps to create additional counters throughout your site—each with its own unique configuration. Every counter can have different starting points, animations, and triggers while using the same base script.

How to troubleshoot common Webflow number counter issues

If your counter isn't working as expected, check these common solutions:

  1. Counter doesn't animate
    • Verify the script is properly placed in the custom code section
    • Check that your brix-counter attribute is spelled correctly
    • Ensure start and end values are valid numbers
  2. Animation timing issues
    • Confirm duration values are numbers without units (use "3" not "3s")
    • Check that delay values are properly formatted
  3. Formatting problems
    • Verify your format choice ("comma", "period", or "none")
    • Check prefix/suffix formatting for special characters
  4. Trigger problems
    • For scroll triggers, ensure the element is visible in viewport
    • For click triggers, verify the element is clickable
    • Test different trigger types to isolate the issue

Conclusion

The BRIX Templates Counter attribute brings powerful number animation capabilities to Webflow with minimal setup requirements. Whether you're creating engaging statistics displays, dynamic fundraising trackers, or interactive countdowns, this solution provides the flexibility and reliability you need while maintaining clean, professional design standards.

Need help implementing more advanced counter scenarios or looking to explore custom animation solutions? Our specialized Webflow agency can help create tailored implementations that perfectly match your specific needs. Feel free to reach out to discuss how we can enhance your Webflow project with sophisticated counter animations and other custom features.

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.
BRIX Templates - Email Newsletter with Webflow ResourcesBRIX Templates - Email NewsletterBRIX Templates - Webflow Email Newsletter
How to add URL copy button functionality in Webflow

How to add URL copy button functionality in Webflow

Learn how to add a copy URL button to your Webflow site. Step-by-step guide for implementing easy page sharing without plugins or code.

Feb 19, 2025
How to add copy-to-clipboard functionality to a button in Webflow

How to add copy-to-clipboard functionality to a button in Webflow

Learn how to add copy-to-clipboard button to your Webflow site in 5 minutes or less.

Feb 12, 2025
How to geo-block visitors on your Webflow site using Cloudflare

How to geo-block visitors on your Webflow site using Cloudflare

Complete tutorial explaining how to prevent users from specific countries from accessing your Webflow website.

Feb 12, 2025