Elements

20 / 59 · Structured output

Number ticker

Digits that roll into place as a count updates in real time.

01234567890123456789,012345678901234567890123456789tokens generated

Installation

npx shadcn@latest add "@assistant-ui/elements-number-ticker"

Usage

import { NumberTicker } from "@/components/elements/number-ticker";

<NumberTicker value={1284} label="tokens / sec" />

Props

value*numberNumeric value whose digits roll into place as it changes.
label*stringMono caption rendered under the rolling number.
classNamestringExtra classes merged onto the root.

Source

number-ticker.tsx
"use client";

import { cn } from "@/lib/utils";
import { mono } from "./surfaces";

function RollingDigit({ digit }: { digit: number }) {
  return (
    <span className="inline-flex h-[1.15em] overflow-hidden">
      <span
        className="flex flex-col transition-transform duration-500 ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none"
        style={{ transform: `translateY(-${digit * 1.15}em)` }}
      >
        {Array.from({ length: 10 }, (_, i) => (
          <span key={i} className="h-[1.15em] leading-[1.15]">
            {i}
          </span>
        ))}
      </span>
    </span>
  );
}

export function NumberTicker({
  value,
  label,
  className,
}: {
  value: number;
  label: string;
  className?: string;
}) {
  const formatted = value.toLocaleString("en-US");

  return (
    <div className={cn("flex flex-col items-center gap-2.5", className)}>
      <span
        className="flex text-3xl font-medium tracking-tight tabular-nums"
        aria-label={formatted}
      >
        {formatted.split("").map((char, i) =>
          /\d/.test(char) ? (
            <RollingDigit key={i} digit={Number(char)} />
          ) : (
            <span key={i} className="h-[1.15em] leading-[1.15]">
              {char}
            </span>
          ),
        )}
      </span>
      <span className={cn(mono, "text-foreground/35")}>{label}</span>
    </div>
  );
}