# Count-Up Stats

> Entry- or scroll-triggered number counters with locale-aware formatting, staggered reveals, synchronized progress visuals, and optional replay.

Canonical: https://gsapvault.com/effects/count-up-stats
Live demo: https://gsapvault.com/demos/count-up-stats/index.html

| Property | Value |
|----------|-------|
| Type | effect |
| Tier | paid |
| Price | £5 |
| Difficulty | beginner |
| Plugins | ScrollTrigger |
| Techniques | scroll-reveal, count-up, stagger |
| Uses Lenis | No |

## Overview

A stat counter effect that animates numbers from zero to their target on entry or as they scroll into view. Built on GSAP and ScrollTrigger, it preserves locale-aware thousands separators, currency prefixes, percentage suffixes, decimal precision, and one-time scroll behavior through simple data attributes. The JavaScript reads no class names, so the attributes go straight onto whatever stats markup you already have; the demo is an ordinary lead-figure-plus-supporting-metrics band. Optional fills, SVG rings, and a semantic replay control share the same staggered timeline.

## Features

- Scroll-triggered counting that fires once per stat, never replaying mid-read
- Locale-aware thousands separators via toLocaleString, with custom or disabled separators per counter
- Prefix and suffix support for currency symbols, percentages, and plus signs
- Decimal precision control with GSAP snap so values never show float noise
- Staggered group reveals from a single data-count-stagger attribute on any container
- Card reveals, progress fills, and pathLength-based SVG rings synchronized to each counter
- Optional load trigger and semantic replay control for immediate dashboard sequences
- Custom countup:start and countup:complete events for analytics or chained animations
- Accessible: prefers-reduced-motion users see exact final values immediately, no counting

## Use Cases

- SaaS landing page metrics sections with staggered stat cards
- Agency about pages showing projects delivered and client satisfaction
- Annual report or investor pages with revenue and growth figures
- E-commerce social proof strips with download and review counts
- Nonprofit impact dashboards counting donations and people helped

## Vibe-Code Ready Setup

This effect includes `START-HERE-AI.md`, a product-specific copy-paste setup prompt for Cursor, Claude Code, ChatGPT, GitHub Copilot, Windsurf, and other coding assistants. It tells the assistant to inspect the existing stack, integrate the supplied files, preserve the design, scope selectors, retain accessibility and responsive behaviour, add framework-appropriate GSAP cleanup, and report what it tested.

[How AI-assisted setup works](https://gsapvault.com/vibe-coding)

## How It Works

Each element with data-count-to gets a proxy counter object animated by gsap.to() with a snap value derived from its decimal precision, so the tween only ever produces valid displayable numbers. An onUpdate callback formats the current value with Intl-backed toLocaleString (or a custom separator) plus any prefix and suffix, while optional fill and SVG ring hooks animate in the same timeline.

A group can start immediately with data-count-trigger="load" or retain the default once-only ScrollTrigger behavior. gsap.matchMedia routes reduced-motion users to exact static values, and the synchronous has-js gate falls back to the final HTML when JavaScript or the GSAP CDN is unavailable.

## Integration Preview

How this effect integrates into a page. The full documentation (examples, events, programmatic API, customization guide) ships with the download.

### Quick Start

**1. Add to your HTML `<head>`:**

```html
<script data-cfasync="false">document.documentElement.classList.add('has-js')</script>
<link rel="stylesheet" href="path/to/style.css">
```

The synchronous class prevents a first-paint flash. If GSAP is unavailable, `script.js` removes it so final HTML values remain visible.

**2. Add before closing `</body>` tag:**

```html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/ScrollTrigger.min.js"></script>
<script src="path/to/script.js"></script>
```

**3. Add data attributes to any number element:**

```html
<!-- Single counter -->
<span data-count-to="12000" data-count-suffix="+">12,000+</span>

<!-- Staggered group of stat cards -->
<div class="stats-grid" data-count-stagger="0.15">
  <div class="stat-card" data-count-block>
    <span class="stat-value" data-count-to="98" data-count-suffix="%">98%</span>
    <span class="stat-label">Satisfaction</span>
  </div>
  <div class="stat-card" data-count-block>
    <span class="stat-value" data-count-to="2000000" data-count-prefix="£">£2,000,000</span>
    <span class="stat-label">Revenue</span>
  </div>
</div>
```

Put the final value in the HTML as the element's text. It is the no-JavaScript fallback and what search engines index; the script replaces it with the animated count.

### Options

Set these on each counter element:

| Attribute | Values | Default | Description |
|-----------|--------|---------|-------------|
| `data-count-to` | Any number | `0` | Target value to count to (required) |
| `data-count-duration` | Seconds | `2` | How long the count takes |
| `data-count-decimals` | `0`, `1`, `2`... | `0` | Decimal places shown while counting |
| `data-count-prefix` | Any string | none | Text before the number, e.g. `£`, `$` |
| `data-count-suffix` | Any string | none | Text after the number, e.g. `%`, `+`, `M` |
| `data-count-separator` | Any string, or `none` | Locale default | Thousands separator. Omit for locale-aware grouping, use `none` to disable |

Set these on wrapper elements:

| Attribute | Where | Description |
|-----------|-------|-------------|
| `data-count-stagger` | A container of counters | Groups every counter inside under one scroll trigger, offsetting each by this many seconds (e.g. `0.15`). Attribute with no value uses the `0.15` default |
| `data-count-block` | An ancestor of a counter | Marks the block that reveals alongside the count (a whole stat card, not just the number). Without it, only the counter element itself animates |
| `data-count-trigger="load"` | A stagger container | Starts its timeline on page entry instead of using ScrollTrigger. Omit it to retain the default scroll trigger |
| `data-count-primary` | A count block | Marks the lead stat: it rises from a shorter distance and gets a small scale pulse as it lands |
| `data-count-replay="#id"` | A semantic button | Restarts the load-triggered timeline whose stagger container matches the selector |
| `data-count-replay-message` | The same button | Text announced in the live region on replay. Defaults to `Values counted again.` |
| `data-count-fill` | An element inside a count block | Scales an accent or progress fill from left to right with that counter |
| `data-count-ring="73"` | An SVG stroke inside a count block | Draws a `pathLength="100"` stroke to the supplied percentage with that counter |

### Accessibility

- **Reduced motion**: respects `prefers-reduced-motion`; counters skip the animation entirely and display their exact formatted final values immediately, and stat blocks appear without the fade/rise
- **Exact final values**: `onComplete` writes the precise formatted target to the DOM, so assistive tech and users always end on the true number, never a mid-tween value
- **No-JS and blocked-CDN fallback**: final values live in the HTML, while the `has-js` gate is absent without JavaScript and removed if GSAP cannot load
- **Replay semantics**: the included control is a native button and announces replay status through a polite live region
- **Fires once by default**: scroll-triggered groups retain `once: true`; replay is opt-in for entry-triggered presentations

### Browser Support

Modern browsers (ES6+). Not compatible with IE11.

### Dependencies

- GSAP 3.12+
- ScrollTrigger plugin

## What You Get

- `index.html`: working demo page
- `assets/script.js`: commented, readable source
- `assets/style.css`: effect styles
- `README.md`: full documentation with examples and framework integration notes
- `START-HERE-AI.md`: product-specific copy-paste prompt for AI-assisted setup
- `LICENSE.txt`: standard license terms
- Lifetime updates: re-download anytime from your library

## Get the Code

This is a premium effect. The standard license costs £5 one-time and covers unlimited personal and commercial projects with no attribution required. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy Count-Up Stats](https://gsapvault.com/effects/count-up-stats)
- [Effects & Templates Vault (£39 one-time, best value): every current and future effect and template](https://gsapvault.com/effects)

## Judge the Code Quality First

These related effects are free with complete source published, written to the same production standard (cleanup functions, reduced-motion support, framework-agnostic):

- [Scroll Progress Indicator](https://gsapvault.com/effects/scroll-progress.md): A precise GSAP reading-progress instrument with bar, ring, side rail, percentage, and active chapter feedback.
- [CSS Scroll Reveal](https://gsapvault.com/effects/css-scroll-reveal.md): Native CSS scroll-driven reveals for crisp fade, slide, and scale entrances with accessible static fallbacks and no animation JavaScript.
- [Parallax Hero](https://gsapvault.com/effects/parallax-hero.md): A pinned hero that separates its background photograph, copy, and foreground card into distinct scroll depths from a single scrubbed ScrollTrigger.

---

From [GSAP Vault](https://gsapvault.com): production-ready GSAP animation effects. Full catalog for agents: https://gsapvault.com/llms-full.txt
