npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

number-ticker

v3.0.2

Published

Lightweight TypeScript library for animating numbers with smooth column-scroll transitions. Zero dependencies, framework-agnostic counter animation component for React, Vue, Angular, Web Components, and Vanilla JS.

Downloads

189

Readme

number-ticker

npm version npm downloads license bundle size

Animate numbers elegantly — framework agnostic, zero dependencies.

A lightweight TypeScript library that animates digit changes with smooth column-scroll transitions. Works natively in Vanilla JS, React, Angular, Vue, and as a Web Component.

Preview

alt tag


Table of Contents


Installation

npm install number-ticker
yarn add number-ticker
pnpm add number-ticker

Quick Start

import { NumberTicker } from "number-ticker";
import "number-ticker/dist/number-ticker.css";

const el = document.getElementById("my-ticker");
const ticker = new NumberTicker(el, {
  prefix: "$",
  digitsAfterDecimal: 2,
  currencyFormat: "en-US",
});

ticker.update(1234.56); // animates to $1,234.56

Options Reference

Pass these as the second argument to the constructor. All options are optional.

| Option | Type | Default | Description | | --------------------- | --------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | prefix | string | '' | Static text rendered before the number (e.g. '$', '€', '#'). | | autoWidth | boolean | true | Animates column width as digit count changes (e.g. 910). Disable for fixed-width layouts. | | digitsAfterDecimal | number | 2 | Number of decimal places to display. Set to 0 for integers only. | | currencyFormat | string | '' | A BCP 47 locale string (e.g. 'en-US', 'de-DE') to format the integer part with locale-specific thousand separators. Leave empty to skip. | | numberOnInit | number | 0 | The number shown when the ticker first mounts — appears instantly with no animation. | | timingFunction | string | 'ease' | CSS easing for the digit scroll. Accepts any valid transition-timing-function value. | | transitionSpeed | number | 3 | Controls animation duration. Duration in seconds = 1 / transitionSpeed. Higher = faster. | | transitionDelay | number | 6 | Stagger delay between digit columns. Each digit's delay = sequence / transitionDelay. Higher = less stagger. | | colorOnNumberChange | object | { colorOnIncrease: '', colorOnDecrease: '' } | Temporarily tints digits during animation based on direction of change. Pass any CSS color string. |

colorOnNumberChange Detail

colorOnNumberChange: {
  colorOnIncrease: 'springgreen', // applied when new > old
  colorOnDecrease: 'tomato',      // applied when new < old
}

The color is applied at transitionstart and removed at transitionend automatically.


Public API

constructor(element, options?)

Initializes the ticker on the given DOM element.

new NumberTicker(element: HTMLElement | null, options?: NumberTickerOptions)

| Parameter | Type | Description | | --------- | --------------------- | ------------------------------------------------------------------------------ | | element | HTMLElement \| null | The container element. A null value logs an error and skips init gracefully. | | options | NumberTickerOptions | Optional configuration object (see Options Reference). |


update(number: number)

Animates the ticker from its current value to a new value.

ticker.update(9999.99);
  • Triggers column scroll animations with staggered delays.
  • Adds or removes digit columns automatically as digit count changes.
  • Applies colorOnNumberChange colors if configured.

destroy()

Removes all ticker DOM nodes and resets internal state. Call this on component unmount to prevent memory leaks.

ticker.destroy();

Styles

Import the compiled CSS (or the SCSS source if your bundler supports it):

// Compiled CSS (recommended)
import "number-ticker/dist/number-ticker.css";
// SCSS source (requires a SCSS-capable bundler)
@import "number-ticker/src/NumberTicker.scss";

Customization

The ticker inherits font-size, font-family, font-weight, and color from the host element:

#my-ticker {
  font-size: 3rem;
  font-weight: 700;
  font-family: "Tabular", monospace;
}

Key Class Reference

| Class | Role | | ------------------------ | ------------------------------------------------------------------------ | | .native-ticker | Added to the host element by the library. | | .number-ticker-wrapper | Flex container wrapping the entire ticker. | | .ticker-columns | Row of digit column groups. Gets .auto-width when autoWidth: true. | | .splited-column | Holds columns for the integer part (index 0) and decimal part (index 1). | | .column-wrapper | Clips each digit column via overflow: hidden. | | .column | Scrolls vertically — each child <span> is one character. | | .decimal | The . separator between integer and decimal columns. | | .prefix | Prefix text span. |


Framework Usage

Vanilla JS / TypeScript

import { NumberTicker } from "number-ticker";
import "number-ticker/dist/number-ticker.css";

const ticker = new NumberTicker(document.getElementById("price-ticker"), {
  prefix: "$",
  digitsAfterDecimal: 2,
  currencyFormat: "en-US",
  transitionSpeed: 4,
  colorOnNumberChange: {
    colorOnIncrease: "springgreen",
    colorOnDecrease: "tomato",
  },
});

ticker.update(4299.0);

// Cleanup
ticker.destroy();
<span id="price-ticker"></span>

React

// hooks/useNumberTicker.ts
import { useEffect, useRef } from "react";
import { NumberTicker, NumberTickerOptions } from "number-ticker";

export function useNumberTicker(options?: NumberTickerOptions) {
  const ref = useRef<HTMLSpanElement>(null);
  const ticker = useRef<NumberTicker | null>(null);

  useEffect(() => {
    if (ref.current) {
      ticker.current = new NumberTicker(ref.current, options);
    }
    return () => ticker.current?.destroy();
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  const update = (n: number) => ticker.current?.update(n);
  return { ref, update };
}
// PriceBadge.tsx
import "number-ticker/dist/number-ticker.css";
import { useNumberTicker } from "./hooks/useNumberTicker";

function PriceBadge() {
  const { ref, update } = useNumberTicker({
    prefix: "$",
    digitsAfterDecimal: 2,
    currencyFormat: "en-US",
  });

  return (
    <div>
      <span ref={ref} />
      <button onClick={() => update(1999.99)}>Update Price</button>
    </div>
  );
}

Note: destroy() is called automatically on component unmount.


Angular

// number-ticker.directive.ts
import { Directive, ElementRef, Input, OnDestroy, OnInit } from "@angular/core";
import { NumberTicker, NumberTickerOptions } from "number-ticker";

@Directive({ selector: "[numberTicker]", standalone: true })
export class NumberTickerDirective implements OnInit, OnDestroy {
  @Input() tickerOptions: NumberTickerOptions = {};
  private ticker!: NumberTicker;

  constructor(private el: ElementRef<HTMLElement>) {}

  ngOnInit() {
    this.ticker = new NumberTicker(this.el.nativeElement, this.tickerOptions);
  }

  update(value: number) {
    this.ticker.update(value);
  }

  ngOnDestroy() {
    this.ticker.destroy();
  }
}
<!-- app.component.html -->
<span
  numberTicker
  [tickerOptions]="{ prefix: '€', digitsAfterDecimal: 2, currencyFormat: 'de-DE' }"
  #myTicker="numberTicker"
></span>

<button (click)="myTicker.update(3500)">Update</button>
// app.component.ts
import { Component, ViewChild } from "@angular/core";
import { NumberTickerDirective } from "./number-ticker.directive";

@Component({
  imports: [NumberTickerDirective],
  templateUrl: "./app.component.html",
})
export class AppComponent {
  @ViewChild("myTicker") ticker!: NumberTickerDirective;

  updatePrice() {
    this.ticker.update(2499.99);
  }
}

Note: Import NumberTickerDirective in your imports array (standalone API) or declarations (NgModule). Import the CSS in your global styles.scss.


Vue 3

// composables/useNumberTicker.ts
import { onMounted, onUnmounted, ref, Ref } from "vue";
import { NumberTicker, NumberTickerOptions } from "number-ticker";

export function useNumberTicker(options?: NumberTickerOptions) {
  const elRef: Ref<HTMLElement | null> = ref(null);
  let ticker: NumberTicker | null = null;

  onMounted(() => {
    if (elRef.value) ticker = new NumberTicker(elRef.value, options);
  });

  onUnmounted(() => ticker?.destroy());

  const update = (n: number) => ticker?.update(n);
  return { elRef, update };
}
<script setup lang="ts">
import "number-ticker/dist/number-ticker.css";
import { useNumberTicker } from "@/composables/useNumberTicker";

const { elRef, update } = useNumberTicker({
  prefix: "€",
  digitsAfterDecimal: 2,
  currencyFormat: "fr-FR",
  colorOnNumberChange: {
    colorOnIncrease: "#22c55e",
    colorOnDecrease: "#ef4444",
  },
});
</script>

<template>
  <div>
    <span ref="elRef" />
    <button @click="update(8800)">Animate</button>
  </div>
</template>

Web Component (Zero Framework)

Register the custom element once, then use it in any HTML page — no framework required.

// Register once in your entry file
import "number-ticker/web-component";
import "number-ticker/dist/number-ticker.css";
<!-- Basic integer -->
<number-ticker value="0"></number-ticker>

<!-- Currency with prefix -->
<number-ticker
  value="1234"
  prefix="$"
  digits="2"
  currency="en-US"
></number-ticker>

<!-- Animate via JavaScript -->
<number-ticker id="live-count" value="0" digits="0"></number-ticker>

<script>
  const el = document.getElementById("live-count");
  setInterval(() => {
    el.setAttribute("value", String(Math.floor(Math.random() * 10000)));
  }, 2000);
</script>

Supported HTML attributes:

| Attribute | Maps to option | Example | | ---------- | --------------------------- | ------------------ | | value | numberOnInit / update() | value="1234" | | prefix | prefix | prefix="$" | | digits | digitsAfterDecimal | digits="2" | | currency | currencyFormat | currency="en-US" | | speed | transitionSpeed | speed="4" |

Changing value via setAttribute() triggers animation automatically.


Examples

Stock Price Ticker

import { NumberTicker } from "number-ticker";

const ticker = new NumberTicker(document.getElementById("stock"), {
  prefix: "$",
  digitsAfterDecimal: 2,
  currencyFormat: "en-US",
  transitionSpeed: 5,
  transitionDelay: 4,
  colorOnNumberChange: {
    colorOnIncrease: "#22c55e",
    colorOnDecrease: "#ef4444",
  },
});

setInterval(() => ticker.update(Math.random() * 500 + 100), 3000);

Animated Counter (Integer Only)

const ticker = new NumberTicker(document.getElementById("counter"), {
  digitsAfterDecimal: 0,
  autoWidth: true,
  transitionSpeed: 2,
});

ticker.update(1000000);

Fixed-Width Score Display

const ticker = new NumberTicker(document.getElementById("score"), {
  digitsAfterDecimal: 0,
  autoWidth: false,
  numberOnInit: 0,
  transitionSpeed: 6,
  timingFunction: "cubic-bezier(0.34, 1.56, 0.64, 1)",
});

Browser Support

| Browser | Version | | ------- | ------- | | Chrome | ≥ 79 | | Firefox | ≥ 75 | | Safari | ≥ 13.1 | | Edge | ≥ 79 |

Web Component usage requires browsers with Custom Elements v1 support.


Contributing

# Clone the repo
git clone https://github.com/your-username/number-ticker.git
cd number-ticker

# Install dependencies
npm install

# Build
npm run build

# Watch mode
npm run dev

Pull requests are welcome. For major changes, please open an issue first.


License

MIT © Harsh Rana