# Countup

> A countup animates a number from zero to its final value when it scrolls into view.

Animate numbers with the countup component, for statistics on dashboards and landing pages.

Source: https://docs.tabler.io/ui/plugins/countup

---

## Overview

A countup animates a number from a starting value up to its final value. Use it for statistics on a dashboard or a landing page, where the number is the point of the block.

Write the final number as the text of the element and add `data-countup`. Tabler finds every such element and animates it when it scrolls into view, so no JavaScript of your own is needed.

```html
<h1 data-countup="true">30000</h1>
```

## Installation

Install countup.js with npm:

```shell
npm install countup.js
yarn add countup.js
pnpm install countup.js
bun install countup.js
```

Or include it from a CDN:

```html
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.5.0/dist/libs/countup.js/dist/countUp.umd.js"></script>
```

Tabler reads the library from the `countUp` global, so load the UMD build above before `tabler.js`. If you install with npm and bundle it yourself, assign it first:

```js
import * as countUp from 'countup.js';

window.countUp = countUp;
```

For options beyond the ones below, see the [countUp.js website](https://inorganik.github.io/countUp.js/).

## Usage

Add `data-countup` to any text element and write the target number inside it. The animation starts as soon as the number enters the viewport.

```html
<h1 data-countup>30000</h1>
```

Pass options as JSON in the same attribute. Every option below works this way.

```html
<h1 data-countup='{"duration":4,"suffix":"%"}'>300</h1>
```

### Duration

`duration` sets how long the animation takes, in seconds. The default is 2.

```html
<h1 data-countup="true">30000</h1>
<h1 data-countup="{&quot;duration&quot;:4}">30000</h1>
<h1 data-countup="{&quot;duration&quot;:6}">30000</h1>
```

### Starting value

`startVal` sets the starting value; the default is zero. A start value above the final one makes the number count down instead of up.

```html
<h1 data-countup="{&quot;startVal&quot;:12345}">30000</h1>
<h1 data-countup="{&quot;startVal&quot;:47655}">30000</h1>
```

### Decimal places

`decimalPlaces` sets how many decimals are shown. The default is 0.

```html
<h1 data-countup="true">3.123</h1>
<h1 data-countup="{&quot;decimalPlaces&quot;:1}">3.123</h1>
<h1 data-countup="{&quot;decimalPlaces&quot;:2}">3.123</h1>
<h1 data-countup="{&quot;decimalPlaces&quot;:3}">3.123</h1>
```

### Easing

Easing is on by default, so the animation slows down towards the end. Set `"useEasing": false` for a linear animation.

```html
<h1 data-countup="true">30000</h1>
<h1 data-countup="{&quot;useEasing&quot;: false}">30000</h1>
```

### Use grouping

Grouping is on by default, so thousands get a separator. Set `"useGrouping": false` to turn it off.

```html
<h1 data-countup="true">30000</h1>
<h1 data-countup="{&quot;useGrouping&quot;: false}">30000</h1>
```

### Separator

`separator` sets the thousands separator.

```html
<h1 data-countup="true">3000000</h1>
<h1 data-countup="{&quot;separator&quot;:&quot; &quot;}">3000000</h1>
```

### Decimal separator

`decimal` sets the decimal separator.

```html
<h1 data-countup="{&quot;decimalPlaces&quot;:2}">3.12</h1>
<h1 data-countup="{&quot;decimalPlaces&quot;:2,&quot;decimal&quot;:&quot;,&quot;}">3.12</h1>
```

### Prefix

`prefix` adds text before the number, for example a currency symbol.

```html
<h1 data-countup="{&quot;prefix&quot;:&quot;$&quot;}">30000</h1>
<h1 data-countup="{&quot;prefix&quot;:&quot;€&quot;}">30000</h1>
```

### Suffix

`suffix` adds text after the number, for example a percent sign.

```html
<h1 data-countup="{&quot;suffix&quot;:&quot;%&quot;}">300</h1>
<h1 data-countup="{&quot;suffix&quot;:&quot;‰&quot;}">300</h1>
```

## JavaScript

Tabler automatically initializes all elements with `data-countup` on page load. This is the code that runs:

```ts
const countupElements: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>('[data-countup]')

if (countupElements.length) {
  countupElements.forEach(function (element: HTMLElement) {
    let options: Record<string, any> = {}
    try {
      const dataOptions = element.getAttribute('data-countup') ? JSON.parse(element.getAttribute('data-countup')!) : {}
      options = Object.assign(
        {
          enableScrollSpy: true,
        },
        dataOptions,
      )
    } catch (error) {
      // ignore invalid JSON
    }

    // Strip thousands separators, currency symbols and other non-numeric characters
    // so formatted targets like "1,234", "1 234" or "$99.5" parse correctly.
    const value = parseFloat((element.textContent ?? '').replace(/[^0-9.-]/g, ''))

    if (!Number.isNaN(value) && window.countUp && window.countUp.CountUp) {
      const countUp = new window.countUp.CountUp(element, value, options)
      // When scrollSpy is enabled CountUp starts the animation itself once the
      // element scrolls into view, so only start manually when it's disabled.
      if (!countUp.error && !options.enableScrollSpy) {
        countUp.start()
      }
    }
  })
}
```

_Source: `core/js/src/countup.ts`_

## Accessibility

- Write the final number in the HTML, as the examples do. That is what a search engine indexes, what shows without JavaScript, and what a screen reader reads if it reaches the element before the animation starts.
- Never put a countup inside an `aria-live` region. The text changes on every frame, so a live region would announce dozens of intermediate numbers.
- The number on its own says nothing. Give it a visible label next to it, or an `aria-label` on the block that holds both.
- Tabler does not check `prefers-reduced-motion`, so a countup animates even for users who asked for less motion. Skip the initialization for them:

```js
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  document.querySelectorAll('[data-countup]').forEach((el) => el.removeAttribute('data-countup'));
}
```

Run it before `tabler.js`, so the number simply stays at its final value.
