# Animated Number Counter Shortcode

> Adds a [wpide_counter to="2500" suffix="+"] shortcode that counts up from zero when the number scrolls into view, for statistics and milestones.

- Type: PHP
- Runs: Run everywhere
- Category: [Frontend & Design](https://wpide.com/library/category/frontend/)
- Tags: counter, animation, shortcode, statistics
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/frontend-animated-counter-shortcode/

## Code

```php
add_action( 'wp_enqueue_scripts', function () {
    wp_register_script( 'wpide-counter', false, array(), '1.0', true );
    wp_add_inline_script( 'wpide-counter', <<<'JS'
(function () {
    var counters = document.querySelectorAll('.wpide-counter[data-to]');
    if (!counters.length) { return; }
    var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    function format(el, value) {
        var decimals = parseInt(el.getAttribute('data-decimals') || '0', 10);
        var text = value.toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
        el.querySelector('.wpide-counter__value').textContent = text;
    }
    function run(el) {
        var from = parseFloat(el.getAttribute('data-from')) || 0;
        var to = parseFloat(el.getAttribute('data-to')) || 0;
        var duration = parseInt(el.getAttribute('data-duration') || '1600', 10);
        if (reduce) { format(el, to); return; }
        var start = null;
        function step(time) {
            if (start === null) { start = time; }
            var p = Math.min(1, (time - start) / duration);
            var eased = 1 - Math.pow(1 - p, 3);
            format(el, from + (to - from) * eased);
            if (p < 1) { requestAnimationFrame(step); }
        }
        requestAnimationFrame(step);
    }
    if (!('IntersectionObserver' in window)) { counters.forEach(run); return; }
    var io = new IntersectionObserver(function (entries) {
        entries.forEach(function (entry) {
            if (entry.isIntersecting) { io.unobserve(entry.target); run(entry.target); }
        });
    }, { threshold: 0.4 });
    counters.forEach(function (el) { format(el, parseFloat(el.getAttribute('data-from')) || 0); io.observe(el); });
})();
JS
    );
} );

add_shortcode( 'wpide_counter', function ( $atts ) {
    $atts = shortcode_atts( array( 'from' => 0, 'to' => 100, 'prefix' => '', 'suffix' => '', 'duration' => 1600, 'decimals' => 0 ), $atts, 'wpide_counter' );

    wp_enqueue_script( 'wpide-counter' );

    $to       = (float) $atts['to'];
    $decimals = min( 4, absint( $atts['decimals'] ) );

    return sprintf(
        '<span class="wpide-counter" data-from="%1$s" data-to="%2$s" data-duration="%3$d" data-decimals="%4$d" style="font-variant-numeric:tabular-nums;">%5$s<span class="wpide-counter__value">%6$s</span>%7$s</span>',
        esc_attr( (float) $atts['from'] ),
        esc_attr( $to ),
        absint( $atts['duration'] ),
        $decimals,
        esc_html( $atts['prefix'] ),
        esc_html( number_format_i18n( $to, $decimals ) ),
        esc_html( $atts['suffix'] )
    );
} );
```

## Notes

Options: from, to, prefix, suffix, duration (ms) and decimals. Visitors who prefer reduced motion see the final number straight away.

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Animated Number Counter Shortcode" in its snippet library and add it in one click; or create a new PHP snippet in WPIDE (or any code snippets plugin) and paste the code above. More snippets: https://wpide.com/library/
