Scrolling Number

A wheel of numbers that spins to its value and settles under a highlight.

1,000
"use client"

import * as React from "react"
import {
  ScrollingNumber,
  ScrollingNumberContainer,
  ScrollingNumberHighlight,
  ScrollingNumberItems,
  type ScrollingNumberDirection,
} from "@animate-ui/react/primitives/texts/scrolling-number"
import { RefreshIcon } from "@hugeicons/core-free-icons"
import { HugeiconsIcon } from "@hugeicons/react"
import { cn } from "cn"

import { Button } from "@/components/ui/button"

export default function ScrollingNumberDemo({
  direction = "btt",
  delay = 1000,
}: {
  direction?: ScrollingNumberDirection
  delay?: number
}) {
  const [run, setRun] = React.useState(0)
  const isVertical = direction === "btt" || direction === "ttb"

  return (
    <div className="flex flex-col items-center gap-6">
      <ScrollingNumberContainer
        key={`${run}-${direction}-${delay}`}
        direction={direction}
        number={1000}
        step={100}
        itemsSize={isVertical ? 40 : 75}
        className={cn(
          "text-lg font-medium tabular-nums",
          isVertical ? "w-18" : "h-10"
        )}
      >
        <ScrollingNumber delay={delay}>
          <ScrollingNumberItems className="text-muted-foreground flex items-center justify-center" />
        </ScrollingNumber>
        <ScrollingNumberHighlight className="bg-muted size-full rounded-md" />
      </ScrollingNumberContainer>
      <Button variant="outline" size="sm" onClick={() => setRun((n) => n + 1)}>
        <HugeiconsIcon icon={RefreshIcon} strokeWidth={2} data-icon="inline-start" />
        Replay
      </Button>
    </div>
  )
}

Installation

npm install @animate-ui/react motion
pnpm add @animate-ui/react motion
yarn add @animate-ui/react motion
bun add @animate-ui/react motion

Usage

import {
  ScrollingNumber,
  ScrollingNumberContainer,
  ScrollingNumberHighlight,
  ScrollingNumberItems,
} from '@animate-ui/react/primitives/texts/scrolling-number';

export function Score() {
  return (
    <ScrollingNumberContainer number={1000} step={100} itemsSize={40}>
      <ScrollingNumber>
        <ScrollingNumberItems />
      </ScrollingNumber>
      <ScrollingNumberHighlight />
    </ScrollingNumberContainer>
  );
}

Animation

  • After delay, the strip spins through every step up to number on a spring (stiffness: 90, damping: 30): fast through the range, then it settles without overshoot.
  • A new number springs on from where the strip is.
  • The strip is written by a motion value, without a React render per frame. onNumberChange reports the value under the highlight as it passes.
  • <AnimateUIProvider tempo> paces the default spring.
  • Under reduced motion, the wheel lands on the number right away.
  • The strip is hidden from screen readers, which read the number once from the container.

API Reference

ScrollingNumberContainer

PropTypeDefault
number
number-
step
number-
itemsSize?
number30
sideItemsCount?
number2
direction?
'btt' | 'ttb' | 'ltr' | 'rtl''btt'
onNumberChange?
(value: number) => void-
inView?
booleanfalse
inViewMargin?
string'0px'
inViewOnce?
booleantrue
...props?
React.ComponentProps<'div'>-

ScrollingNumber

PropTypeDefault
delay?
number0
onCompleted?
() => void-
transition?
Transition{ stiffness: 90, damping: 30 }
...props?
HTMLMotionProps<'div'>-

ScrollingNumberItems

PropTypeDefault
...props?
Omit<React.ComponentProps<'div'>, 'children'>-

ScrollingNumberHighlight

PropTypeDefault
...props?
React.ComponentProps<'div'>-

useScrollingNumber

Returns the container's state inside it (number, step, range, direction, isInView…).

Built by Skyleen. The source code is available on GitHub.

Last updated: 10/18/2026

On this page