Countup

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

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.

30000

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

Installation

Install countup.js with npm:

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

Or include it from a CDN:

<script src="https://cdn.jsdelivr.net/npm/@tabler/[email protected]/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:

import * as countUp from 'countup.js';

window.countUp = countUp;

For options beyond the ones below, see the countUp.js website.

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.

<h1 data-countup>30000</h1>

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

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

Duration

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

30000

30000

30000

<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.

30000

30000

<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.

3.123

3.123

3.123

3.123

<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.

30000

30000

<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.

30000

30000

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

Separator

separator sets the thousands separator.

3000000

3000000

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

Decimal separator

decimal sets the decimal separator.

3.12

3.12

<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.

30000

30000

<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.

300

300

<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:

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()
      }
    }
  })
}

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:
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.

On this page

130 sleek illustrations for your startup's visual identity.