Charts/Core charts
Stat
<Stat /> · 14 examples
One big number, how it got there, and whether it has moved since you last looked.
Overview
Most dashboards are built by people who love charts, and it shows. You open one to answer a single question, is the thing on fire?, and you are handed a wall of lines, axes, legends and a time picker. The stat panel is the opposite instinct. It takes one value, makes it as large as the tile allows, and gets out of the way.
The format is old and humble: the scoreboard. A speedometer reading, the number on a petrol pump, the total at the bottom of a receipt. Dashboards inherited it as the "single stat", and Grafana's Stat panel is the version most people have seen: a headline number, a faint sparkline behind it for context, and a colour that changes when the number crosses a line you care about. This chart is a port of that design, so if you know it, you already know this.
The sparkline is what keeps it honest. A bare number says 94%. A number with a sparkline says 94%, and it was 40% an hour ago. Same digits, very different Tuesday.
Props
API
<Stat> shows one big number per value: the current CPU, today's revenue, the error count. It can add a sparkline of the history, colour the number or the whole tile, and show the change since the start. It is a port of Grafana's Stat panel; see Coming from Grafana.
use zopra_charts::*;
Not sure which option fits? See the guide.
<Stat>
<Stat data={samples} value={|s: &Sample| s.cpu} x={|s: &Sample| s.time} format={percent} />| Prop | Type | Default |
|---|---|---|
data | Vec<T>, Signal<Vec<T>>, Frame<N>, Signal<Frame<N>>, or your own Dataset | required |
value | Fn(&T) -> f64 | every field of a Frame |
name | Fn(&T) -> &str: the name of each row, with Reduce::All | field names for a Frame |
reduce | Reduce | Reduce::Calc(Calc::Last) |
fields | &'static [&'static str]: which fields of a Frame | every field except x_field |
x | Fn(&T) -> f64: the time of each row, for the sparkline | the row number |
x_field | field name: a Frame's time field, used for the sparkline and not shown as a value | none |
color_mode | StatColorMode | Value |
graph_mode | GraphMode | Area |
justify_mode | JustifyMode | Auto |
text_mode | TextMode | Auto |
orientation | BarOrientation | Auto |
wide_layout | bool | true |
show_percent_change | bool | false |
percent_change_color | PercentChangeColor | Standard |
title_size, value_size, percent_size | Pixels | fitted to the tile |
min, max | f64 | the smallest and largest number in the data |
thresholds | Thresholds | your theme's good colour, then its bad colour from 80 |
color | GaugeColor | Thresholds |
mappings | &'static ValueMappings | none |
format | Fn(f64) -> SharedString | none |
decimals | usize | up to 3, trailing zeros dropped |
class, key |
Which values are shown
reduce decides how many tiles there are:
| Your data | reduce | Tiles |
|---|---|---|
rows with value | Reduce::Calc(calc) (default Last) | one tile: calc over every row, with a sparkline |
rows with value and name | Reduce::All { limit } | one tile per row, no sparkline |
a Frame | Reduce::Calc(calc) | one tile per field, each with its own sparkline |
a Frame | Reduce::All { limit } | one tile per cell |
Layout
Each tile picks one of Grafana's four layouts from its size:
| Tile | Layout |
|---|---|
| more than 2.5 times wider than tall, with a sparkline and taller than 50 px | name on the left, value on the right, sparkline in the bottom half |
| more than 2.5 times wider than tall, otherwise | name and value side by side, centred vertically |
| taller than 100 px, with a sparkline | name above value, sparkline filling the rest |
| otherwise | name above value, centred vertically |
wide_layout={false}always uses the stacked layouts.justify_mode={JustifyMode::Center}centres the text. Tiles without a name are always centred.- Text is sized to fit. With several tiles, every tile uses the size that fits the longest name and value, so they line up.
title_size,value_sizeandpercent_sizefix the sizes instead. text_mode:Autoshows only the value for a single tile and the name and value for several.Nameshows the name in place of the value;Noneshows neither and leaves the colour and sparkline.- Several tiles.
BarOrientation::Autopacks tiles into the grid that keeps them closest to square.Horizontalstacks them as rows andVerticalplaces them side by side.
Colour
The value's colour comes from a mapping if one matches, otherwise from color: the threshold step it reaches, its position on a scheme between min and max, a palette colour per tile, or one fixed colour.
color_mode | Effect |
|---|---|
Value | the number in the value's colour |
Background | the tile filled with a gradient of the value's colour, text in black or white for contrast |
BackgroundSolid | the tile filled with the value's colour |
None | the number in your theme's text colour |
The sparkline uses the value's colour, or white on a background. GraphMode::Area fills under the line; Line draws only the line; None hides it.
Percent change
With show_percent_change, each tile reduced from several rows shows the change from the first to the last number, as Grafana computes it: (last - first) / first × 100. An arrow shows the direction.
percent_change_color | Colour |
|---|---|
Standard | green when it rises, red when it falls or stays the same |
Inverted | red when it rises, green when it falls: for errors, latency, cost |
SameAsValue | the value's colour |
No change is shown when the first number is 0.
Enums
| Type | Values |
|---|---|
StatColorMode | Value (default), Background, BackgroundSolid, None |
GraphMode | Area (default), Line, None |
JustifyMode | Auto (default), Center |
PercentChangeColor | Standard (default), Inverted, SameAsValue |
TextMode | Auto (default), ValueAndName, Value, Name, None |
BarOrientation | Auto (default), Horizontal, Vertical |
Coming from Grafana
| Grafana option | Here |
|---|---|
| Calculation, Fields, Show (calculate / all values), Limit | reduce, fields |
| Orientation | orientation |
| Text mode, Wide layout | text_mode, wide_layout |
| Color mode, Graph mode, Text alignment | color_mode, graph_mode, justify_mode |
| Show percent change, Percent change color mode | show_percent_change, percent_change_color |
| Text size: title, value, percent | title_size, value_size, percent_size |
| Min, Max, Thresholds, Color scheme, Value mappings | min, max, thresholds, color, mappings |
| Unit, Decimals | format, decimals |
Not available here: units from Grafana's unit registry, data links and the panel's time range for the sparkline (it spans the data instead).
Run them
14 examples
Press Run to load an example into the live render at the top of the page. Open one to read its code.
How to read it
- 01Read the number first. The tile is sized so that it is the biggest thing on the screen.
- 02Check the colour. It is the threshold step the value reached, so green or red tells you whether to keep reading.
- 03Glance at the sparkline for direction only. Each sparkline is scaled to its own range, so two tiles with similar-looking lines can differ by orders of magnitude.
- 04If the percent change is on, it compares the first and last readings in the tile, and the arrow shows the direction. There is nothing to click through to: the tile is the whole answer, which is the point.
When to use it, and when not to
Reach for it when
- You have one question per tile and the answer is a number: current CPU, orders today, open incidents, p99 latency.
- The reader will glance at it from across a room or on a wall monitor. A background colour (
BackgroundSolid) is readable from further away than any axis. - You want a row of KPIs with a bit of history each. Pass a
Frameand you get one tile per field, with matching text sizes. - The status is a code, not a quantity: map
200to OK and503to Down with value mappings and let the tile show the word.
Skip it when
- The shape of the history is the story. A sparkline has no axis and no labels on purpose, if people need to read values off the line, use Time Series.
- You want the number against a known range with limits drawn on it. That is what Gauge is for.
- You are comparing many values with each other. Tiles of different colours are hard to rank. A Bar Gauge lines them up on one scale.
- The number is only meaningful with its distribution around it, such as a latency that is fine on average and terrible at the tail.
The math behind it
From rows to one number
Take a column of readings in time order. A stat tile first reduces them to a single value with the calculation you picked in reduce. The default is the last finite reading. Others are the mean, minimum, maximum or sum.
Non-finite readings (missing samples, NaN) are skipped before reducing. If you ask for Reduce::All instead, nothing is reduced: every row becomes its own tile and there is no sparkline.
Percent change and the sparkline
With show_percent_change, the tile shows the change from the first finite reading to the last finite reading , the same way Grafana does. Note that this is the first-to-last change, not the change of the reduced value. If the quotient is not finite and nothing is shown.
The sparkline maps time to the tile's width and value to its height. Writing for the time range and for the value range of that tile's own data, a point lands at the fractions below. If the series is flat, the range is widened by 10% of its magnitude (or set to 0 to 1 when the value is zero) so the line sits in the middle instead of dividing by zero.
Colour and layout
The colour comes from a value mapping if one matches, otherwise from the thresholds: the step whose lower bound is the largest one not above . Percentage thresholds are resolved against min and max, which default to the smallest and largest number in the data.
Each tile then picks one of four layouts from its own shape. With width and height , a tile counts as wide when . A wide tile with a sparkline and px puts the name left, the value right and the sparkline in the bottom half. A tall tile with a sparkline needs px to stack name, value and sparkline. Text is sized to fit by measuring it, and with several tiles every tile takes the size that fits the longest name and value, so the row lines up.
Real life usage
The dashboard nobody had to open
Marguerite ran support operations for a company that sold smart thermostats, which is to say she answered for every house that was too cold at 6 a.m. and every customer who had opinions about it. Her team's wall monitor showed a Grafana-style board of eleven panels, and by her own count three of them had never been looked at on purpose.
The trouble was the morning stand-up. It started at 9:00 and spent the first eight minutes on one question: how bad is the queue? Somebody would lean toward the screen, squint at a multi-line chart of ticket volume and say something like "looks a bit higher than yesterday?", which is what people say about every chart.
Marguerite wanted four numbers a person could read from the coffee machine: open tickets, median first response in minutes, tickets older than a day, and today's volume against the same hour last week. Each needed a colour that meant something, and each needed just enough history to separate "high" from "high and climbing".
She built the board in a small desktop app using zopra-charts. A single <Stat> fed with a frame of the four fields gave her four equal tiles, text sized to the longest label, sparklines underneath. She set thresholds per field, switched the colour mode to BackgroundSolid, and flipped percent_change_color to Inverted, because for ticket counts a rise is bad news and a tile that turned green when things got worse would have been funny exactly once.
The first morning, the aged-tickets tile was a deep, unmissable red with a 62% arrow beside it. Nobody leaned toward the screen. Somebody said "it's the weekend backlog" from the doorway, and that was the whole discussion.
Over the next month the stand-up lost its first eight minutes. Median first response dropped from 41 to 27 minutes, mostly because a red tile is difficult to defer and a line chart is easy to. Aged tickets stayed under 30 for three straight weeks, a number that had not previously been seen in the wild.
The other seven panels are still on the old board, as a monument. The wall monitor now shows four squares, and the squares are the only thing anyone has ever complained about not being large enough.
Pitfalls
- Reduce chooses the story.
Lastis "now",Meanis "over this period",Maxis "the worst moment". The same data gives three different numbers, so say in the title which one you picked. - Sparklines are scaled per tile. A flat-looking line on a tile whose values range between 99.1 and 99.3 is as dramatic as a spike elsewhere. Do not compare slopes across tiles.
- Percent change is first to last. If the series dipped and recovered, the change is small even though the middle was ugly. And a first value of zero produces no change at all.
- Colour direction. Up is not always good. For errors, latency and cost use
PercentChangeColor::Inverted, and set thresholds so that the high end is the bad end. - Fixed text sizes. Setting
value_sizestops the text from fitting the tile. That is useful when panels must match, and clipped when the number grows an extra digit.
From around the web