1 slot open this monthBy the makers of shadcnregistryWe built shadcnregistry. Now we build your product.Book a callCheck availability
1 slot open this monthAI features?We build AI features into your product.Book a callCheck availability
1 slot open this month🚨 Slop police🚨 Slop police: we clean up vibe-coded apps.Report itReport your app
shadcnregistry
shadcnregistry
RegistryHealthInspect
Terms of ServicePrivacy Policy
  1. Registries
  2. pulld
  3. Animated Number

Animated Number

ui

A number that counts to its value instead of appearing at it, for a figure meant to be looked at: a dashboard KPI or metric tile, a results, votes, signups, downloads, streams or follower count, a pricing page's savings or annual-total figure, a fundraising or goal tracker, a score, streak or XP total, a usage or quota readout, and the statistic in a marketing hero that should count as it scrolls into view. Common asks it answers: "animated counter react", "count up animation react", "number ticker component", "animated number shadcn", "shadcn counter", "react-countup alternative", "odometer react", "rolling number animation", "kpi counter animation", "count up on scroll react", "animate number when it changes react", "intersection observer counter react", "useCountUp hook", "animated number currency format", "animate a formatted number react", "counter jitters while animating", "number width jumps while counting up", "tabular nums counter", "prefers-reduced-motion counter", "animated number without framer motion", "数字 カウントアップ react", "数値 アニメーション", "カウントアップ アニメーション", "スクロールで数字を数え上げる", "桁が揺れる カウンター". Official shadcn/ui has nothing of the kind, and the figure is measured rather than remembered: its catalogue today lists 65 entries of which 62 are fetchable (code-block, questionnaire and trace 404) for 223,287 bytes of component source, and in all of it requestAnimationFrame, IntersectionObserver, NumberFormat, prefers-reduced-motion and countUp are every one of them zero hits. tabular-nums appears exactly twice -- on a chart tooltip's value and a sidebar badge's count -- and both of those numbers sit still, so nothing in official has ever had to make a number move. An agent asked for this therefore writes the requestAnimationFrame loop from scratch, and the loop it writes makes the line the number sits on move. That is the whole reason to install one. Interpolating towards 12,480 renders 1,204 and then 11,593 on the way, and in a proportional font a 1 is not as wide as an 8, so the number changes width every frame and drags the label beside it along; then the group separator arrives at 1,000 and it jumps again. tabular-nums fixes the digits, but the separator and the digit count are not fixed by anything, so the widest string the run can produce is rendered invisibly underneath in the same grid cell and holds the width open from the first frame. That widest string is found by formatting the points where the width can actually change -- each power of ten and the all-nines value below it -- rather than by formatting the two ends, because the ends are not the widest: counting 0 to 1000 in compact notation finishes at "1K" and passes through "999", and under scientific notation it passes through "9.99E2" where both ends are three characters. It is measured over the declared journey rather than over the live value, too, because re-measuring every frame narrows the box while counting down -- the box would shrink under the number it is holding open, which is the shift it exists to prevent. Four more things separate it from the twenty-line version. It interpolates the number and formats each frame, rather than interpolating the formatted string, so a currency symbol, a locale's group separators and a percent sign all survive the trip -- and the decimals are pinned to what the endpoints need, because Intl.NumberFormat's default of up to three fraction digits renders 1,203.882 then 1,229.44 then 1,234.5 on the way to a value whose own length never changes. A float's tail is read as the precision it meant rather than literally, so 0.1 + 0.2 counts to 0.3 and not to 0.30000000000000004, while anything the caller says about digits -- fraction or significant, currency, percent or compact -- is left exactly as given, since fraction digits can outrank significant ones under morePrecision. prefers-reduced-motion means the value simply appears, and it is read from the media query at the moment the decision is made rather than from state, because state is a render behind and a flag that arrives in a passive effect animates one frame for somebody who asked for none; the preference is also subscribed to, so turning it on mid-count finishes the count. A target that changes while the number is still moving is picked up from wherever the number currently is instead of restarting at zero, which is the ordinary case on a dashboard that polls. And the first render is the real number, on the server and the client alike, so the HTML a crawler or a page with JavaScript turned off receives says 12,480 -- the starting value is put in place before the first paint, so the final number is never shown and then counted up to. It is accessible in the one way that matters here: the counting digits are hidden from the accessibility tree and the target is exposed as text, because a number announced sixty times a second is not a reading of a number. Set startOnView to hold at the starting value until the element has been scrolled to, which counts once and then stops observing; a browser without IntersectionObserver counts immediately rather than sitting at zero forever, and one without requestAnimationFrame shows the value, which is also what the server does. useAnimatedNumber() is exported for a figure of your own design, handing back the current frame, the formatted target and the width to hold open; easing is a plain function so an inline one costs nothing (it is read live rather than listed as a dependency, which is what stops a run restarting on every render it causes); onComplete reports each arrival once per target. Within pulld it is the counterpart to countdown, which moves towards a moment in time rather than a value, and it is the thing to put inside stat-card, where the figure lives; number-input collects a number, gauge, ratio-bar and sparkline draw one as a shape, and this one moves the digits themselves. Depends only on your cn util -- no animation library, no extra packages.

pnpm dlx shadcn@latest add "https://pulld.pages.dev/r/animated-number.json"
Premium blocks for shadcn/uiShadcnblocks.com · AffiliateExplore
components/ui/animated-number.tsx