---
title: Key Value Pattern
description: Composing editable key-value forms and displaying them in readonly mode as badges.
icon: tags
author:
  name: eblairmckee
  url: https://github.com/eblairmckee
mode: wide
---

Key-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.

<ComponentPreview name="pattern-key-value-demo" />

## Editable Mode

Use `KeyValueField` for the editable form. It handles dynamic row management, duplicate key detection, and focus management out of the box.

<ComponentPreview name="key-value-field-demo" description="A key-value field with pre-populated pairs." />

### 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.

<ComponentPreview name="key-value-field-creatable-demo" description="Key-value field with creatable comboboxes." />

### 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.

<ComponentPreview
  name="key-value-field-validate-demo"
  description="Form validation with keyValuePairsSchema and React Hook Form."
/>

## 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.

```tsx
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 = [
  { id: 'tag-environment', key: 'environment', value: 'production' },
  { id: 'tag-region', key: 'region', value: 'us-east-1' },
];

<BadgeGroup
  items={tags.map((tag) => ({
    id: tag.id,
    textValue: `${tag.key}: ${tag.value}`,
    content: (
      <Badge variant="outline">
        {tag.key}: {tag.value}
      </Badge>
    ),
  }))}
  maxVisible={3}
  renderOverflowContent={(overflow) => (
    <List>
      {overflow.map((item) => (
        <ListItem key={item.id}>{item.textValue}</ListItem>
      ))}
    </List>
  )}
  variant="outline"
/>;
```

## 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

1. **Limit visible badges** — Use `maxVisible` on `BadgeGroup` to keep layouts compact. 3–5 is a good default depending on available space.
2. **Use the `outline` variant** — Keeps badges visually lightweight for metadata display. Reserve colored variants for status indicators.
3. **Filter empty keys** — Before rendering readonly badges, filter out pairs with empty keys so incomplete entries don't leak into the display.
4. **Cap items in forms** — Use the `maxItems` prop on `KeyValueField` to prevent unbounded growth. Common limits are 10–50 depending on the resource type.
5. **Validate before submission** — Use `validateKeyValuePairs` to catch duplicates and empty keys before the form is submitted, not just on blur.
6. **Show the diff on save** — When editing existing tags, use `getKeyValueDiff` to show users what will change before they confirm.
