---
title: Meter
description: Small bars inside rows and panels. No axes; the number beside each bar carries its value.
---

```tsx
import { Meter } from "@openperf/deft";

import { Preview } from "../../src/Preview";

export default function MeterBasic() {
  return (
    <Preview>
      <Meter.Root value={0.48} />
    </Preview>
  );
}
```

- Use a meter for one value per row, inside a table, list or panel. For a value over time, use a [Sparkline](/deft/components/sparkline) or a [Chart](/deft/components/chart).
- `Meter.Root` prints `value`, a fraction from 0 to 1, as a percentage and fills the bar to `value / max`; `max` defaults to 1.
- For row-relative bars, give every row the same `max`, usually the largest row's value, so the largest row fills its bar and the others stay comparable. Set `showValue={false}` when the percent would read as the bar's length.
- Use the same scale for bars that people compare.
- Print percentages and values as text; a bar is never the only place a number lives.

## Range

```tsx
import { Meter } from "@openperf/deft";

import { Preview } from "../../src/Preview";

export default function MeterRange() {
  return (
    <Preview>
      <Meter.Range domain={[10, 1000]} p50={20} p95={120} p99={480} />
      <Meter.Range domain={[10, 1000]} failing={true} p50={120} p95={480} p99={1000} />
    </Preview>
  );
}
```

`Meter.Range` draws p50 to p95 as the main segment, p95 to p99 as the tail and a tick at p95. The scale is logarithmic, so `domain` starts above zero; values outside it are clamped to the edges. Share `domain` across rows. Set `failing` only when a row exceeds its limit; red is reserved for failing rows.

## Distribution

```tsx
import { Meter } from "@openperf/deft";

import { Preview } from "../../src/Preview";

export default function MeterDistribution() {
  return (
    <Preview stack={true}>
      <Meter.Distribution fair={20} good={70} poor={10} />
    </Preview>
  );
}
```

Use `Meter.Distribution` for the good, fair and poor parts of a web vital. Each segment's width is its share of the three values, so percentages and fractions both work; a part of 0 is left out. Keep the percentages as text next to the bar; colour and segment width cannot carry the values alone. Use `size="sm"`, a thinner bar, inside table rows.

## Part to whole

```tsx
import { Meter } from "@openperf/deft";

import { Preview } from "../../src/Preview";

export default function MeterParts() {
  return (
    <Preview stack={true}>
      <Meter.Parts
        parts={Array.from({ length: 4 }, (_, index) => ({
          id: `part-${index + 1}`,
          label: `Part ${index + 1}`,
          value: 48 / 2 ** index,
        }))}
      />
    </Preview>
  );
}
```

`Meter.Parts` draws the parts in the order given, up to seven segments: with more than seven, the first six stay and the rest are grouped as Other. Pass them largest first. Segment `n` takes colour `n` of `partsRampClassNames`, so a table next to the bar can show the same swatch per row. The legend prints each value as a number; pass `formatValue` to show a percentage instead, and set `legend={false}` to draw the bar alone. Hover a segment or a legend item to highlight its counterpart.

## Compare

```tsx
import { Meter } from "@openperf/deft";

import { Preview } from "../../src/Preview";

export default function MeterCompare() {
  return (
    <Preview stack={true}>
      <Meter.Compare
        current={{ label: "Current", text: "120", value: 120 }}
        previous={{ label: "Previous", text: "48", value: 48 }}
      />
    </Preview>
  );
}
```

`Meter.Compare` puts the current period above the previous one, each as a row of its label, a bar and its value as text. Both bars share one scale, the larger of the two values. The value column keeps a minimum width, so the bars of meters stacked in a column start and end together. The current bar takes `tone`, `story`, `warning` or `danger`, to match the metric's health; the previous one stays in the grey of a data bar.

## Scale

```tsx
import { Meter } from "@openperf/deft";

import { Preview } from "../../src/Preview";

export default function MeterScale() {
  return (
    <Preview stack={true}>
      <Meter.Scale good={48} poor={120} value={72} />
    </Preview>
  );
}
```

`Meter.Scale` shows where a value sits against two limits, for a metric where lower is better, such as a web vital. The track has three equal segments: up to `good`, from `good` to `poor`, and past `poor`. The marker moves through each segment in proportion to `value` and stops at the end of the track, at one and a half times `poor`, so scales with different limits line up. Leave `value` out when there is no data. Print the value as text next to the scale.

## API

| Prop | Type | Default | Description |
| - | - | - | - |
| `value` | `number` | - | On Root, a fraction from 0 to 1. On Scale, optional. |
| `max?` | `number` | `1` | On Root. |
| `showValue?` | `boolean` | `true` | On Root. |
| `p50` | `number` | - | On Range. Also `p95` and `p99`. |
| `domain` | `[number, number]` | - | On Range. Both above 0. |
| `failing?` | `boolean` | `false` | On Range. |
| `good` | `number` | - | On Distribution. Also `fair` and `poor`. |
| `size?` | `"sm" \| "md"` | `"md"` | On Distribution. |
| `parts` | `{ id: string; label: ReactNode; value: number }[]` | - | On Parts. |
| `formatValue?` | `(value: number, total: number) => ReactNode` | - | On Parts. |
| `legend?` | `boolean` | `true` | On Parts. |
| `current` | `{ label: ReactNode; text: ReactNode; value: number }` | - | On Compare. Also `previous`. |
| `tone?` | `"story" \| "warning" \| "danger"` | `"story"` | On Compare. |
| `poor` | `number` | - | On Scale. Also `good`, in the units of `value`. |

Parts: `Root`, `Compare`, `Distribution`, `Parts`, `Range`, `Scale`. `partsRampClassNames` holds the segment colours of `Parts`, in order, for swatches that match them. Other props go to the native element.
