Skip to main content

KeyValue

Every example on this page is live, drawn by the library's Storybook, where you can also change its props and see the result.

Displays a list of key-value pairs in vertical (definition list) or horizontal (side-by-side) layout.

Import​

import { KeyValue } from "~lib/data-display/key-value";

Props​

NameTypeRequiredDefaultDescription
dataKeyValueRow[] | KeyValueSection[]YesN/AThe rows, or sections of rows each under a heading
orientation"vertical" | "horizontal"No"vertical"Layout direction
dividerbooleanNotrueShow dividers between rows, and between columns with columns
size"xs" | "sm"No"sm"Text size and row spacing
truncatebooleanNofalseKeep every key and value to one line ending in "…", the key giving way first
columnsnumberNoOne columnSplit the rows across up to this many columns, down the first before the next
minColumnWidthnumberNo160Narrowest a column gets, in pixels, before the list drops to fewer columns

A KeyValueRow is one fact:

NameTypeRequiredDescription
keystringYesThe fact's name
valueReactNodeNoThe fact's value. Left out or empty, the key has the row to itself
descriptionstringNoA second line under the key at the xs size, past any leading node
leadingReactNodeNoSits before the key, centered on its line
trailingReactNodeNoSits beside the value
donebooleanNoStrikes the key, the description and the value's text through, for a finished task

A KeyValueSection is { heading: string; rows: KeyValueRow[] }: the heading is set as a bold xs line above its rows.

Examples​

Vertical​

Open Vertical in Storybook

Horizontal​

Open Horizontal in Storybook

No Divider​

Open No Divider in Storybook

Compact​

Use size="xs" in narrow columns: smaller text and tighter rows fit more facts in the same height.

Open Compact in Storybook

With Trailing Content​

Give a row trailing content, such as a Badge or a Sparkline, to show it beside the value instead of writing it into the value's text.

<KeyValue
orientation="horizontal"
data={[{ key: "AAPL", value: "$517.53", trailing: <Badge>▲ 0.92%</Badge> }]}
/>
Open With Trailing in Storybook

With Leading Content​

Give a row leading content, such as a Badge for a train's line, to show it before the key, centered on the key's line, the way a departure board reads.

<KeyValue
orientation="horizontal"
data={[{ key: "Howard", value: "7:53 pm", leading: <Badge>Red</Badge> }]}
/>
Open With Leading in Storybook

Vertical Rows With Leading and Trailing Content​

A vertical row draws leading before the key, centered on its line, in a column of its own, so the value lines up under the key's text; trailing sits beside the value. A to-do list down a slim column keeps the same row shape as one across a band: the tick box before the task, the overdue badge beside its date.

<KeyValue
size="xs"
truncate
data={tasks.map((task) => ({
key: task.name,
value: task.due,
leading: <Icon name={task.done ? "square-check" : "square"} size={18} />,
trailing: task.overdue ? <Badge size="xs">Overdue</Badge> : undefined,
done: task.done,
}))}
/>
Open Vertical With Leading And Trailing in Storybook

With a Description​

Give a row a description to set a smaller second line under its key, at the xs size whatever the list's size, such as the stop a departure leaves from. It starts past any leading node, so it lines up with the key's text, and the value stays on the key's line. With truncate it is kept to one line ending in "…" like the key. The description is part of its row, so every row still keeps whole in columns and the columns balance to one height; a done row strikes its description through with the rest of its text.

<KeyValue
orientation="horizontal"
size="xs"
truncate
data={departures.map((departure) => ({
key: departure.destination,
description: `from ${departure.stop}`,
value: departure.due,
leading: <Badge size="xs">{departure.line}</Badge>,
}))}
/>
Open With Description in Storybook
Open Descriptions In Columns in Storybook

Done​

Set done on a row to strike its key, its description and its value's text through, in full ink, the way a finished task or a bought item is crossed off. Its leading and trailing content, and a value that is a node of its own such as a Badge, are left as they are.

<KeyValue
orientation="horizontal"
data={[
{ key: "Oat milk", value: "2 L", done: true },
{ key: "Eggs", value: "12" },
]}
/>
Open Done in Storybook

No Value​

Leave a row's value out, or empty, for a fact that is a name alone, such as an ingredient with no amount: the key has the whole row to itself rather than stopping short of a blank column.

<KeyValue
orientation="horizontal"
data={[
{ key: "Plain flour", value: "200 g" },
{ key: "A pinch of sea salt, or to taste" },
]}
/>
Open No Value in Storybook

Truncated​

Keys and values wrap by default, so a long key such as an ingredient reads in full. Set truncate to keep every key and value to one line ending in "…" instead, so each row keeps one height and a box that counts its rows never meets a row that grew. The key gives way first: in a horizontal row the value is cut short only once the key has no room left, and leading and trailing content is never cut.

<KeyValue
orientation="horizontal"
truncate
data={[
{
key: "Harlem/Lake via the Loop",
value: "8:04 pm",
leading: <Badge>Green</Badge>,
},
]}
/>
Open Truncated in Storybook

Columns​

Set columns to split a long list into columns side by side, such as a line-status board in a wide, short box. The rows run down the first column and then the next, every column is the same width, and a rule sits between them (divider={false} leaves only a gap). Use it with truncate, and give every row the same kind of content, such as a badge in each, so the rows are one height and line up across the columns.

<KeyValue
orientation="horizontal"
size="xs"
truncate
columns={3}
data={lines.map((line) => ({
key: line.name,
value: <Badge variant="outline">{line.status}</Badge>,
}))}
/>
Open Columns in Storybook

A box too narrow for every column at minColumnWidth gets fewer columns. The browser works this out from the box, so a saved preview shown in a narrower box drops a column the same way.

Open Fewer Columns In A Narrow Box in Storybook

The columns are balanced to one height, so in a Brim a list taller than its box is cut across every column at the same line, under the fade.

<Brim>
<KeyValue
orientation="horizontal"
size="xs"
truncate
columns={3}
data={rows}
/>
</Brim>
Open Columns In A Brim in Storybook

Sections​

Pass data as sections, each a heading over its rows, to group a list the way it is read, such as a statement's payments under the day they were made. Each heading is a bold xs line across the list with a rule above it and none between it and its own rows, so the rules keep one rhythm from the first row to the last.

<KeyValue
orientation="horizontal"
size="xs"
truncate
data={[
{
heading: "Today",
rows: [
{ key: "Tesco Express", value: "−£12.40" },
{ key: "Pret A Manger", value: "−£5.10" },
],
},
{ heading: "Yesterday", rows: [{ key: "Spotify", value: "−£11.99" }] },
]}
/>
Open Sections in Storybook

With columns the sections flow down one column and into the next the way rows do, balanced to one height, and a heading never stays at the foot of a column without its first row. In a Brim the list is cut across every column at the same line.

Open Sections In Columns in Storybook

See Also​

  • Table - Full table display
  • Stat - Single metric
  • List - Simple lists