Skip to content
KONIGI Patterns

Dashboards / Page layout / Header KPI strip

‹ 2 of 5 ›

Header KPI strip

People need a quick summary of the measures that require attention.

Updated September 10, 2026

Problem

People opening a dashboard need a quick way to see whether anything needs attention.

Solution

Place a short row of key values at the top. Use the same label, value, and context positions in each tile so the row is easy to scan.

Choose metrics that affect what the audience does next. Include a useful comparison, target, or trend rather than relying on a large number alone.

Keep the row focused. If too many metrics qualify, group them or move detail into another level. Show missing or stale data clearly, especially when color signals that everything is fine.

Use when

A small set of measures tells people whether they need to investigate further.

Don’t use when

The page is analytical. On an exploratory dashboard the summary numbers are the least interesting thing on screen, and putting them first trains people to scroll past the top of the page.

Trade-offs

The strip uses prominent space while showing limited detail. Adding more tiles makes priorities less clear. Current values can miss rapid changes, and incorrect status rules can give false reassurance.

Checklist

  • Would a viewer act differently depending on each of these values? If not, why is it here?
  • How many tiles, and does the row still read as a row?
  • Do all tiles share label position, number size, and delta placement exactly?
  • Does each tile carry context—a delta, a target, a sparkline—or just a number?
  • Is state encoded so the odd one out is findable without reading?
  • Where do the threshold colours come from, and when were they last reviewed?
  • Do the tiles cover the same time window as each other and as the page?
  • What does the strip show when a source is down?
  • Is the strip readable at wallboard distance if this page ever goes on a wall?
  • What is the process for adding a tile, and is there one for removing?

Compare

Grafana builds the strip from stat panels in the top row of the grid, so uniformity is a matter of whoever configured them using the same options six times, and drift between tiles is the normal state of a dashboard more than a year old. Plausible makes the strip the primary navigation: the top row of metrics is clickable and switches what the main chart plots, which turns the summary into a control rather than a readout. Datadog ties the row to the page’s tag scope so the strip re-scopes with the rest of the page, keeping the summary honest when someone filters to one region. AWS CloudWatch offers single-value widgets with sparkline backgrounds but no shared anatomy, so the row’s coherence depends entirely on the author, which is why most CloudWatch strips look assembled rather than designed.

KPI tile defines the contents of each item. Panel grid arranges the page below it. Semantic status color highlights conditions needing attention.

Delta indicator adds a comparison. Overview then detail connects the summary to investigation.

Header KPI strip anatomy Five tiles across the top of a page, every one sharing the same label position, number size and delta placement, so the row reads as one object. Below, the same five values with five different layouts, which reads as five things. One shape, five times Error rate 0.04% 0.01 P95 312ms 24ms Checkouts 4,182 3.1% Queue depth 7 12 Budget left 61% 9 pts 1 2 1 SHARED ANATOMY Label, number and delta on the same baselines across all five. The eye reads the row as one object and stops only where something differs. 2 ACTIONABLE, NOT BIG A number belongs here if someone would do something different depending on its value. Total requests is important and rarely that. Four to six reads as a row. Nine reads as a grid, and a grid has no reading order. The same five numbers, five layouts Error rate 0.04% 312ms P95 latency Checkouts 4,182 Queue 7 Budget left 61% Same five values. Now it is five things to read instead of one row to scan.
Wireframe — the pattern's anatomy, not any one product's version of it

Implementation

The first row answers whether it is okay before the rest asks why. Four tiles is the ceiling worth defending; a fifth means somebody could not choose and the row stops being scannable.

shadcn
npx shadcn@latest add card
Tokens
--card--card-foreground--accent--muted-foreground--border--ring--direction-up--direction-down--direction-flat

vs previous hour

KpiStrip.tsxOne anatomy repeated. The row is typed at four to six tiles, every tile has to say what someone does when it moves, the window belongs to the strip, and a source that is down says so.

import { Card } from "@/components/ui/card";
import { directionColor, directionGlyph, type Polarity } from "../red-green-direction/direction";

export type Tile = {
  label: string;
  /** null is a source that is down. The tile says so instead of showing the
   *  last number it had. */
  value: string | null;
  /** Signed change over the strip's window. `text` is the change in the unit
   *  the label uses ("24ms", "9 pts"); `value` carries the sign. */
  delta: { value: number; text: string };
  polarity?: Polarity;
  /** What someone does differently when this moves. Required, because the
   *  test for a place in the strip is actionability rather than importance,
   *  and a tile that cannot answer it is a total wearing a KPI's clothes. */
  action: string;
};

/** Four to six reads as a row. Nine reads as a grid, and a grid has no reading
 *  order, so the type stops at six. */
export type Row =
  | [Tile, Tile, Tile, Tile]
  | [Tile, Tile, Tile, Tile, Tile]
  | [Tile, Tile, Tile, Tile, Tile, Tile];

export function KpiStrip({ tiles, window, onSelect }: {
  tiles: Row;
  /** One window for the whole row. Tiles cannot be compared over different
   *  periods, so it is a property of the strip rather than of a tile. */
  window: string;
  onSelect?: (tile: Tile) => void;
}) {
  return (
    <div>
      <Card className="grid divide-x" style={{ gridTemplateColumns: `repeat(${tiles.length}, minmax(0, 1fr))` }}>
        {/* One anatomy, every time: label, number, delta, on the same three
            baselines. The eye stops only where a value differs. */}
        {tiles.map((t) => {
          const polarity = t.polarity ?? "higher-is-better";
          return (
            <button
              key={t.label}
              type="button"
              title={t.action}
              onClick={() => onSelect?.(t)}
              className="px-4 py-3 text-left hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
            >
              <p className="text-[11px] uppercase tracking-wide text-muted-foreground">{t.label}</p>
              {t.value === null ? (
                <>
                  <p className="mt-1.5 text-2xl tabular-nums text-muted-foreground">—</p>
                  <p className="mt-1 text-[11px] text-muted-foreground">source down</p>
                </>
              ) : (
                <>
                  <p className="mt-1.5 text-2xl tabular-nums text-card-foreground">{t.value}</p>
                  <p className="mt-1 text-[11px] tabular-nums" style={{ color: directionColor(t.delta.value, polarity) }}>
                    <span aria-hidden="true">{directionGlyph(t.delta.value)} </span>
                    {t.delta.text}
                  </p>
                </>
              )}
            </button>
          );
        })}
      </Card>
      <p className="mt-1.5 text-right text-[10px] text-muted-foreground">{window}</p>
    </div>
  );
}

demo.tsxHow it is called: five checkout tiles against the previous hour. Hover a tile for the action it exists for.

import { KpiStrip, type Row } from "./KpiStrip";

/** Five tiles for a checkout service, each with the thing it changes. */
const TILES: Row = [
  { label: "Error rate", value: "0.04%", delta: { value: -0.01, text: "0.01" }, polarity: "lower-is-better", action: "Above 0.5% the release is rolled back." },
  { label: "P95", value: "312ms", delta: { value: 24, text: "24ms" }, polarity: "lower-is-better", action: "Above 400ms the on-call engineer is paged." },
  { label: "Checkouts", value: "4,182", delta: { value: 3.1, text: "3.1%" }, action: "A drop of 10% against last week opens an incident." },
  { label: "Queue depth", value: "7", delta: { value: -12, text: "12" }, polarity: "lower-is-better", action: "Above 50 a second worker is started." },
  { label: "Budget left", value: "61%", delta: { value: -9, text: "9 pts" }, action: "Below 25% non-urgent deploys are frozen." },
];

export default function Demo() {
  return <KpiStrip tiles={TILES} window="vs previous hour" onSelect={() => {}} />;
}
What it renders. Identical markup in both panes, with only the token values changing.

Examples

Screenshots are grouped by product and include the capture date. Dashed boxes highlight this pattern. Notes appear below each screenshot and when you hover over a box.

Tableau Public

Published workbooks show how individual authors arrange charts and interactions in Tableau.

User Funnel & Conversion Rates September 11, 2026 Tableau Public embed view; workbook published by Tetiana Berezhna medium · light · desktop-web
This published workbook shows overall conversion at each funnel step but omits conversion from the preceding step. Readers must calculate that themselves to compare losses between stages. The tapered rendering makes comparisons harder than aligned bars would. Another panel places rates based on very different registration counts at the same visual weight, showing why the denominator matters.
  • Funnel The tapered shape makes step quantities harder to compare than aligned bars.
  • Funnel Only overall conversion is labeled; step-to-step conversion requires calculation.
  • Ratio and rate Similar-looking rates are based on very different registration counts.
  • Header KPI strip Summary counts appear without a comparison value.
  • Tabs as genres Worksheet tabs use metric names that may need explanation.
Show 9 more examples Hide the rest

Tableau Public

Published workbooks show how individual authors arrange charts and interactions in Tableau.

Shopify Customer Journey September 10, 2026 Tableau Public embed view; workbook published by Lovelytics medium · light · desktop-web
This Lovelytics workbook uses clickable metrics and bars to filter or reveal detail. Instructions explain these interactions because the marks do not clearly look interactive. Nine summary values compete for attention in a grid. The sales chart also has a mismatch between its monthly title and the displayed dates, while its truncated vertical scale emphasizes variation. These choices make labels and interaction cues especially important.
  • Cross-filter An instruction explains that metric tiles filter the dashboard.
  • Header KPI strip Nine values share a grid without a clear starting priority.
  • Time series The title and displayed period differ, and the vertical axis starts above zero.
  • Hover detail City details are described as available on hover.
  • Geo map with markers The map shows raw sales totals, which also reflect differences between populations.
  • Ranked list One much larger value leaves the remaining bars difficult to compare.

Grafana

Examples of panel grids, shared selectors, stat tiles, and chart configuration.

Grafana SLO / SLO Overview September 10, 2026 Grafana Play (signed out; no version string exposed) dense · dark · desktop-web
The overview lists twenty-eight objectives with an indicator, remaining budget, and sparkline for each. The budget column combines large negative percentages with positive values, but gives little explanation of how to read them. No data uses the same red as breached objectives, making missing information harder to distinguish from a failed target. The sparklines also use different scales, so their shapes cannot be compared directly across rows.
  • Metric targets The budget column adds context to each objective’s result.
  • Target and progress Large negative budget values need a clear explanation of the calculation.
  • Error and stale state No data uses the same red treatment as a breach.
  • Small multiples Sparkline scales differ between rows, limiting direct comparison.
  • Header KPI strip The top tiles count targets, objectives, and series rather than showing whether goals are met.
Examples / Stats September 10, 2026 Grafana Play (signed out; no version string exposed) dense · dark · desktop-web
Grafana’s stat showcase compares value colors, background fills, layouts, and text options. Most examples emphasize presentation without adding a comparison value or clear reference point. The No text panel uses color alone, leaving cells without labels or numbers. These are useful demonstrations of settings, but a working dashboard needs enough context to explain each value and state.
  • KPI tile A label, value, unit, and sparkline appear without an explicit comparison.
  • Dark-first data palette The dark theme makes these value colors more prominent than in the light capture.
  • Sparkline Sparklines use individual scales, so row-to-row comparison needs care.
  • Semantic status color Whole-tile color strongly emphasizes every value in this group.
  • Grayscale with alerts Multiple strong colors compete for attention.
  • KPI tile Values appear without labels explaining what they measure.
  • Semantic status color The cells have no text, leaving color as the only visible cue.
  • Header KPI strip Consistent label and value positions make the tile row easier to scan.
Linux node / fleet overview September 9, 2026 Grafana Play (signed out; no version string exposed) medium · dark · desktop-web
The data source was returning no measurements when this overview was captured. A red tile says no metrics were received and suggests checking configuration. The freshness tile says No data, while another panel says No errors. Those messages need clearer distinctions: missing measurements do not establish that a system has no errors.

Home Assistant

Room-based card layouts combine household device states with controls.

Demo dashboard at phone width September 10, 2026 Home Assistant public demo (signed out, 390px viewport) medium · light · mobile
The desktop dashboard reflows into a narrow phone layout while keeping its room sections and controls. Cards remain in two columns within sections, limiting the page’s length. The section order differs from the desktop columns, so the responsive layout changes what people encounter first. Navigation moves behind a menu button, while the summary chips remain at the top.
Demo dashboard September 10, 2026 Home Assistant public demo (signed out) medium · light · desktop-web
Cards are grouped by rooms such as Living room, Kitchen, and Study. States are written in words, with icon color providing a second cue. Section headers add useful room summaries, such as temperature or meeting status. The page also contains controls, including lighting sliders and thermostat buttons, so people can act in the same place they check the state.

Kibana

A query bar and filtering controls define the data shown in charts and document tables.

Dashboards / [Flights] Global Flight Dashboard September 10, 2026 Elastic demo environment, sample flight data (guest session) dense · light · desktop-web
The delay table shows percentages without the number of flights behind each one. A high rate based on very few flights would look as prominent as one based on many. A nearby comparison tile names the previous week but does not clearly explain what its percentage represents. The filtering controls are more explicit: a query bar sits above pickers and a price range, giving people both typed and guided ways to narrow the view.
  • Ratio and rate Delay percentages appear without their flight counts.
  • Compare periods The comparison period is named, but the meaning of the percentage is unclear.
  • Share and embed Sharing and full-screen controls are available in the header.
  • Filter bar Pickers and a range control provide alternatives to typing a query.
  • Panel grid Panels use different sizes within a grid.
  • Stacked composition A 100% stack shows the mix of delay types without total volume.
  • Annotation Grouped event markers share the chart’s time axis.
  • Header KPI strip Different tile sizes and arrangements make the summary row less consistent.

Netdata

A large collection of system charts with per-chart controls and anomaly-rate views.

Metrics / System September 10, 2026 Netdata Agent v2.10.0-686-nightly (public registry node, signed out) dense · dark · desktop-web
The navigation reports 720 charts organized by the metrics collected from the system. The main page follows the same nested structure, giving broad coverage but little indication of which charts matter most to a particular task. Search helps navigate that volume. The header also distinguishes live and stale nodes, making missing updates visible instead of presenting every node as current.

Plausible Analytics

A metric row controls the main chart, followed by focused traffic breakdowns.

Live demo / plausible.io September 10, 2026 Plausible live demo, plausible.io's own stats (signed out) medium · light · desktop-web
The metric row controls the main chart: the selected tile is outlined and its series appears below. Supporting sections then break the results down by source, page, geography, and other categories. Ranked lists show counts and bars, but no visible share of the total. The map uses very pale colors for most countries, limiting how much variation is easy to see.