Key Value Pattern
Composing editable key-value forms and displaying them in readonly mode as badges.
Made by eblairmckeeKey-value pairs are common in resource management UIs — labels, tags, environment variables, HTTP headers. This pattern covers how to compose the editable form and how to present the data in readonly contexts.
Editable Mode
Use KeyValueField for the editable form. It handles dynamic row management, duplicate key detection, and focus management out of the box.
With Predefined Options
When keys or values come from a known set, use mode: 'combobox' with creatable: true to let users pick from suggestions or type custom values.
With Validation
Use keyValuePairsSchema from input-utils with React Hook Form + Zod to validate key-value pairs on form submission. The schema checks for duplicate keys, empty keys, character patterns, and length limits.
Readonly Mode
When displaying key-value data outside a form — in detail views, tables, or summaries — render them as badges inside a BadgeGroup. Use maxVisible to cap the visible count and show the rest in an overflow tooltip.
import { Badge } from "@/components/redpanda-ui/badge"
import { BadgeGroup } from "@/components/redpanda-ui/badge-group"
import { List, ListItem } from "@/components/redpanda-ui/typography"
const tags = [
{ key: "environment", value: "production" },
{ key: "region", value: "us-east-1" },
];
<BadgeGroup
maxVisible={3}
renderOverflowContent={(overflow) => (
<List>
{tags.slice(-overflow.length).map((tag) => (
<ListItem key={tag.key}>
{tag.key}: {tag.value}
</ListItem>
))}
</List>
)}
variant="simple-outline"
>
{tags.map((tag) => (
<Badge key={tag.key} variant="simple-outline">
{tag.key}: {tag.value}
</Badge>
))}
</BadgeGroup>Anatomy
Editable mode
┌─────────────────────────────────────┐
│ KeyValueField │
│ ├── Label + Description │
│ ├── Row [Key input] [Value input] ✕ │
│ ├── Row [Key input] [Value input] ✕ │
│ └── [+ Add] button │
└─────────────────────────────────────┘
Readonly mode
┌─────────────────────────────────────┐
│ Label │
│ BadgeGroup │
│ ├── Badge "env: production" │
│ ├── Badge "region: us-east-1" │
│ └── +2 more (overflow tooltip) │
└─────────────────────────────────────┘Best Practices
- Limit visible badges — Use
maxVisibleonBadgeGroupto keep layouts compact. 3–5 is a good default depending on available space. - Use
simple-outlinevariant — Keeps badges visually lightweight for metadata display. Reserve colored variants for status indicators. - Filter empty keys — Before rendering readonly badges, filter out pairs with empty keys so incomplete entries don't leak into the display.
- Cap items in forms — Use the
maxItemsprop onKeyValueFieldto prevent unbounded growth. Common limits are 10–50 depending on the resource type. - Validate before submission — Use
validateKeyValuePairsto catch duplicates and empty keys before the form is submitted, not just on blur. - Show the diff on save — When editing existing tags, use
getKeyValueDiffto show users what will change before they confirm.
Built by malinskibeniamin. The source code is available on GitHub.