Rolling number

Free · MITReact Native · Expo

A number whose digits roll in the direction of change, with digits that open and close in place and currency signs that glide instead of jumping.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add https://goodui.dev/native/r/rolling-number.json

Then run npx expo install --fix so react-native-reanimated match your Expo SDK. Files land in components/goodui-native/rolling-number.

Usage

import { View } from "react-native";
import { Text } from "@/components/goodui-native/foundation";
import { RollingNumber } from "@/components/goodui-native/rolling-number/rolling-number";

<View style={{ flexDirection: "row", alignItems: "flex-end", gap: 8 }}>
  <RollingNumber
    value={yearly ? 79.99 : 9.99}
    size="largeTitle"
    weight="medium"
    locale="en-US"
    format={{ style: "currency", currency: "USD" }}
  />
  <Text tone="secondary" style={{ marginBottom: 6 }}>
    {yearly ? "/ year" : "/ month"}
  </Text>
</View>

When to use

Use it for
  • Prices that change with a plan or billing period.
  • Totals, balances, counters and scores that update in place.
Reach for something else
  • Static numbers that never change: use Text with tabular digits.
  • Values that update many times a second: use a plain Text.

API reference

PropTypeDefault
value*
The number to show. Changes roll the digits.
number—
format
Intl options, for example `{ style: "currency", currency: "EUR" }`.
Intl.NumberFormatOptions—
locale
BCP 47 locale for separators and currency placement, for example `de-DE`. Defaults to the device locale.
string—
size
Type size from the theme scale.
"caption" | "footnote" | "subhead" | "body" | "title3" | "title2" | "title1" | "largeTitle""body"
weight
Regular or medium.
"regular" | "medium""regular"
tone
Text color from the theme.
"success" | "warning" | "default" | "danger" | "accent" | "secondary" | "tertiary""default"
trend
Which way digits roll. `auto` rolls up when the value grows and down when it shrinks, even across a 9 to 0 boundary.
"auto" | "up" | "down""auto"
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • Each digit is a strip that rolls on the morph spring; increases roll up and decreases roll down, even across 9 and 0.
  • A digit that joins opens from zero width and one that leaves closes, so text beside the number reflows smoothly.
  • A new currency or locale morphs: the old sign closes while the new one opens.
  • Reduced motion: values change in place without rolling.
  • One Text node per digit. Rolling runs on the UI thread; joining and leaving digits animate their real width.

Accessibility

  • Read as one piece of text with the fully formatted value as its label; the individual digits are hidden.
  • On the web the number is visually hidden text and the digit strips are aria-hidden.

Notes for AI

  • Pass a number, not a string. Format with the format and locale props (Intl.NumberFormat options).
  • It sizes to its content; place labels beside it in a row and they move with it.
  • It is a View, so alignItems: 'baseline' does not line it up with neighboring text. Use a row with alignItems: 'flex-end' and give a smaller label a marginBottom of about 6 pt at largeTitle (see the usage example).
  • Use the same size and weight tokens as Text.