---
title: Key/Value Field Array
description: A shared form fragment for repeated text key/value pairs.
component: true
fragment: true
---
## Usage
Use `KeyValueFieldArray` when each row is two text inputs backed by `react-hook-form`, such as HTTP headers, query parameters, or configuration parameters.
```tsx
import { KeyValueFieldArray } from 'ui-patterns/form/KeyValueFieldArray/KeyValueFieldArray'
import { getKeyValueFieldArrayValidationIssues } from 'ui-patterns/form/KeyValueFieldArray/validation'
```
```tsx
({ name: '', value: '' })}
keyPlaceholder="Header name"
valuePlaceholder="Header value"
addLabel="Add header"
/>
```
`KeyValueFieldArray` owns the row add/remove behavior and renders the per-input form messages for you. Compose it inside `FormItemLayout` when you want the standard label, description, and message treatment around the entire section.
## Validation
`KeyValueFieldArray` is rendering-only. Keep validation in the consumer schema and use the shared validation helper when you want the standard draft-row behaviour:
- fully empty rows may remain drafts
- partially filled rows should show inline errors on the missing cell
If you persist the array rows directly, strip fully empty draft rows before saving them.
```tsx
const formSchema = z
.object({
headers: z.array(z.object({ name: z.string().trim(), value: z.string().trim() })),
})
.superRefine((data, ctx) => {
getKeyValueFieldArrayValidationIssues({
rows: data.headers,
keyFieldName: 'name',
valueFieldName: 'value',
keyRequiredMessage: 'Header name is required',
valueRequiredMessage: 'Header value is required',
}).forEach((issue) => {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: issue.message,
path: ['headers', ...issue.path],
})
})
})
```
## When to use it
- Use [Single Value Field Array](./single-value-field-array) for repeated single values such as redirect URIs.
- Use `KeyValueFieldArray` for repeated text/text pairs such as headers, parameters, and config entries.
- Build a custom row UI instead when each row mixes different controls, such as a text input paired with a `Select`.