Statistic

A number with a label, for dashboards and summaries, and a timer that counts down to a deadline or up from a start.

Usage

When to use Statistic, what its title, value, prefix and suffix are each for, and the choices it leaves to the page around it.

When to use

Statistic shows one number and says what it is: a muted title, a formatted value in heading type, and a prefix or a suffix on either side of it. Use it when a single figure is the point of the surface.
  • A headline figure on a dashboard tile: revenue this month, active users, uptime, tokens spent.
  • A summary above the thing it summarizes, where the chart or the table underneath carries the detail.
  • A deadline or an elapsed time, through Statistic.Timer, which looks the same and counts.
Use something else when:
  • The question is a trend. One number cannot answer “is this going up”, and a delta beside it only answers it for one step. Use a chart.
  • There are many values. Six Statistics in a column is a Descriptions list done the hard way. Descriptions aligns the labels, wraps by itself and stays readable at a smaller size.
  • The number sits inside a sentence. “3 of 12 seats remaining” is prose, and setting the 3 in heading type breaks the line it belongs to. Write plain text.
  • The figure is a share of a whole and the whole is the point. That reads better as a Progress with the number beside it.

The title, and what a label has to say

title takes any node and renders above the value in the muted description color at body size. Leave it out and there is no header at all, which leaves a bare number with nothing saying what it counts.
A good label answers three things:
  • What is counted, in the product's own words. “Bookings”, not “Count”.
  • Over what period, whenever the number moves. “Bookings” and “Bookings this month” are different figures, and the reader cannot tell them apart from the value.
  • In what unit, unless a prefix or a suffix already carries it. Money without a currency is the one that costs real money.
Keep the titles in one row parallel, with the same part of speech and the same period. They are read as a set, and the odd one out reads as a different measure.

Value, precision and formatter

value is a number or a string, and it defaults to 0. A figure that has not loaded yet shows as zero instead of a blank, so set loading from the same request.
  • precision cuts, it does not round. 1.456 at precision={2} shows 1.45, where Intl gives 1.46. On money and percentages that difference shows, so round the value yourself first.
  • groupSeparator defaults to , and decimalSeparator to .. They are fixed strings, and Statistic does not localize, so changing both is not the same as formatting for a locale.
  • A string that is not a plain number shows as written. Pass a string from Intl.NumberFormat for a locale's full format. A numeric string also keeps trailing zeros: "99.980" stays 99.980, where the number shows 99.98.
  • formatter takes over the value and returns a node. Use it when the rule is yours (a compact 4.8M, a duration, an Intl.NumberFormat instance) instead of reaching for it with separators.
  • valueRender wraps the value in your own markup, like a Tooltip. It passes you a React element, so wrap it in a <span> for the Tooltip to attach to.

Prefix and suffix

Both take any node and sit beside the value with a small gap. They are where the unit goes, and keeping the unit out of value lets the number stay a number that Statistic can group and cut.
  • prefix for what comes before the figure: a currency symbol, a trend arrow, a small avatar.
  • suffix for what follows it: %, / 1,000, ms, a Tag carrying the delta.
  • Statistic draws no icon of its own, so an arrow in a prefix comes from whichever icon pack your project installs.
  • Keep it to one extra node per side. A prefix, a suffix and a delta all at once turn a tile into a table row.
  • A sentence goes under the value, not in suffix. The suffix shares the value's line and heading size, which suits a unit and not “from $84,120 last month”.

Color, and what it is allowed to mean

valueStyle is deprecated and warns in development. Put a class on a part instead: classNames.value colors the number alone, and classNames.content colors the number with its prefix and suffix. A theme class like text-success follows the theme into dark mode.
  • Never let color carry direction alone. Green and red say nothing to a reader who cannot tell them apart. Pair the color with an arrow in the prefix and a sign on the number.
  • Pick one meaning per color and keep it across the page. Up is not always good: churn going up is red, and a tile that colors every rise green is wrong about half of them.
  • Color the exceptions, not everything. A row where every number is colored has no emphasis left.
  • classNames also takes a function, ({ props }) => ({ … }), called with the Statistic's props. Use it to keep a threshold next to the class it picks.

Timer, and the Countdown that is not here

Statistic.Timer looks like a Statistic and counts. type is countdown or countup. It replaces Statistic.Countdown, which is deprecated and which the kit leaves out.
  • value is a timestamp. Write Date.now() + 10_000, not 10_000.
  • Create that timestamp once, in a useState initializer. A fresh Date.now() in the render body restarts the clock on every render. A module-level constant starts counting when the module loads, not when the reader arrives.
  • format defaults to HH:mm:ss and takes dayjs tokens, with […] for literal text. Show milliseconds only where they mean something: a ticking SSS keeps the page moving.
  • onFinish runs once, when a countdown reaches zero, and never on a count-up. A new value starts the timer again.
  • onChange runs about sixty times a second with the time left, so set state only when it changes.

Several in a row

The usual overview layout is a Statistic inside a size="small", variant="borderless" Card, laid out in a Row and Col grid. Statistic has no container and no padding of its own, so whatever it sits in sets its spacing.
  • Three to five per row. Past that the eye stops comparing and starts scanning, which is what a table is for.
  • Make the set comparable: the same period, the same precision, the same unit treatment. A row mixing a count, a percentage and a currency needs each one to say so.
  • Order them by importance, left to right, and keep that order between visits. A tile that moves is a tile nobody trusts.
  • Give the whole row one loading source. Half a row of placeholders beside half a row of zeros reads as broken data.
  • Statistic's loading keeps the title and replaces only the number. A Card's loading replaces the whole card body, title included.

What a number needs beside it

A figure with no baseline tells the reader little. “1,248” is not good or bad until they know what it was last week and what it is measured in, and the page has to supply that.
  • A comparison. The change against the previous period, as a signed percentage with its arrow, in the suffix or under the value. Say which period it is against, since “+12%” on its own is another unlabeled number.
  • A unit, in the prefix, the suffix or the title. A short one (4.8M, 12k) saves width and loses precision, so put the exact figure in a Tooltip when it matters.
  • A period, and an as-of time whenever the data is not live. A number that went stale without saying so is worse than no number.
  • Where the figure is a share, the total. 94% of eight requests and 94% of eight million are different claims.