# Vue 3 Autocounter: an animated number counter component for Vue 3

> How vue3-autocounter works, how to install it, and how to use it to animate statistics, metrics and prices in Vue 3 and Nuxt apps, with every prop, event and method documented.

## What is vue3-autocounter?

**vue3-autocounter** is an open-source Vue 3 component, published as an npm package, that animates a number from a start value to an end value over a set duration. I built it as a small, reusable way to show statistics, metrics and prices that "count up" when a page loads, without pulling in a large animation library.

It has been downloaded more than <live-counter :end="176000">

176,000

</live-counter>

 times from npm and has <live-counter :end="82">

82

</live-counter>

 stars on GitHub. Both of those numbers are animated by the component itself.

Key features:

- Counts **up or down**. The direction comes from comparing `startAmount` and `endAmount`.
- Configurable **duration** in seconds.
- **Formatting**: decimals, thousands separator, decimal separator, prefix and suffix (for example `$1,250.00 USD`).
- **Control methods**: `start`, `pause`, `resume` and `reset`, available through a template ref.
- **Lifecycle events**: `mounted` and `finished`.
- Written in **TypeScript**, with an SSR-compatible build.

<project-embed src="https://vue3autocounter.cristopherps.dev" title="vue3-autocounter live demo">

The [official vue3-autocounter website](https://vue3autocounter.cristopherps.dev), with live examples of every option.

</project-embed>

## How do you install vue3-autocounter?

Install it from npm:

```sh
npm install vue3-autocounter
```

### Register it globally

Add it to your `main.js` (or `main.ts`) to make it available in every component:

```js
import { createApp } from 'vue'
import Vue3Autocounter from 'vue3-autocounter'
import App from './App.vue'

createApp(App)
  .component('vue3-autocounter', Vue3Autocounter)
  .mount('#app')
```

### Or import it in a single file component

```vue
<script setup>
import Vue3Autocounter from 'vue3-autocounter'
</script>

<template>
  <Vue3Autocounter :startAmount="0" :endAmount="2021" :duration="3" />
</template>
```

## How do you use it?

A complete example with formatting and an event listener:

```vue
<template>
  <vue3-autocounter
    ref="counter"
    :startAmount="0"
    :endAmount="2021"
    :duration="3"
    prefix="$"
    suffix=" USD"
    separator=","
    decimalSeparator="."
    :decimals="2"
    :autoinit="true"
    @finished="onFinished"
  />
</template>
```

This renders `$2,021.00 USD` once the 3-second animation finishes.

## Which props does vue3-autocounter support?

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        startAmount
      </code>
    </td>
    
    <td>
      <code>
        Number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Value the animation starts from.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        endAmount
      </code>
    </td>
    
    <td>
      <code>
        Number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
      
       (required)
    </td>
    
    <td>
      Value the animation ends at.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        duration
      </code>
    </td>
    
    <td>
      <code>
        Number
      </code>
    </td>
    
    <td>
      <code>
        3
      </code>
    </td>
    
    <td>
      Animation length in seconds. Must be greater than 0.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        autoinit
      </code>
    </td>
    
    <td>
      <code>
        Boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Start counting automatically on mount.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        prefix
      </code>
    </td>
    
    <td>
      <code>
        String
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Text shown before the number, e.g. <code>
        $
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        suffix
      </code>
    </td>
    
    <td>
      <code>
        String
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Text shown after the number, e.g. <code>
        USD
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        separator
      </code>
    </td>
    
    <td>
      <code>
        String
      </code>
    </td>
    
    <td>
      <code>
        ','
      </code>
    </td>
    
    <td>
      Thousands separator.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        decimalSeparator
      </code>
    </td>
    
    <td>
      <code>
        String
      </code>
    </td>
    
    <td>
      <code>
        '.'
      </code>
    </td>
    
    <td>
      Decimal separator.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        decimals
      </code>
    </td>
    
    <td>
      <code>
        Number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Number of decimal places. Must be 0 or greater.
    </td>
  </tr>
</tbody>
</table>

Changing `startAmount`, `endAmount` or `duration` after mount resets the animation and runs it again with the new values. This makes it easy to animate numbers that come from an API.

## How do you control the animation?

Use a template ref to call the component's methods:

```vue
<script setup>
import { ref } from 'vue'

const counter = ref(null)
</script>

<template>
  <vue3-autocounter ref="counter" :endAmount="1000" :autoinit="false" />

  <button @click="counter.start()">Start</button>
  <button @click="counter.pause()">Pause</button>
  <button @click="counter.resume()">Resume</button>
  <button @click="counter.reset()">Reset</button>
</template>
```

<table>
<thead>
  <tr>
    <th>
      Method
    </th>
    
    <th>
      What it does
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        start()
      </code>
    </td>
    
    <td>
      Starts the animation from <code>
        startAmount
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pause()
      </code>
    </td>
    
    <td>
      Pauses the animation and keeps the current value.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        resume()
      </code>
    </td>
    
    <td>
      Continues from the current value for the remaining time.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reset()
      </code>
    </td>
    
    <td>
      Goes back to <code>
        startAmount
      </code>
      
      . It restarts if <code>
        autoinit
      </code>
      
       is <code>
        true
      </code>
      
      .
    </td>
  </tr>
</tbody>
</table>

<table>
<thead>
  <tr>
    <th>
      Event
    </th>
    
    <th>
      When it fires
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        mounted
      </code>
    </td>
    
    <td>
      The component has mounted and is ready.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        finished
      </code>
    </td>
    
    <td>
      The counter has reached <code>
        endAmount
      </code>
      
      .
    </td>
  </tr>
</tbody>
</table>

## How does it work under the hood?

The component uses `window.requestAnimationFrame` instead of `setInterval`. Each frame, it calculates how much of the duration has passed and moves the current value forward by that proportion. This keeps the animation smooth and in sync with the browser's repaint cycle, and the browser pauses it automatically in background tabs.

Formatting runs in a computed property. The number goes through `toFixed(decimals)`, gets split into integer and decimal parts, and the thousands separator is inserted with a regular expression. Because it is computed, the displayed string only recalculates when the value changes.

Pausing stores the remaining time and the current value. `resume()` then starts a new animation from that point, so pause and resume never make the counter jump.

## Using it with Nuxt

The package ships an SSR build, so it works in Nuxt out of the box. Import it in a page or component and the server renders it; the counting starts once the page hydrates in the browser. This site, built with Nuxt, uses vue3-autocounter for the download and star counts above.

<style>

html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}

</style>

---

- [View on npm](https://www.npmjs.com/package/vue3-autocounter)
- [View on GitHub](https://github.com/ps-cristopher/vue3-autocounter)
- [Live demo and docs](https://vue3autocounter.cristopherps.dev)
