KeyValue
The KeyValue field lets users edit a set of key/value pairs. Use it when the saved value should be an associative map (like settings or metadata), or when you only need an ordered list of plain values.
Basic Usage
use InertiaUI\Forms\Fields\KeyValue;
KeyValue::make('metadata')->default([
'environment' => 'production',
'region' => 'eu-west',
]);Keyed mode is the default. It stores an object-like associative array:
{
"environment": "production",
"region": "eu-west"
}The array validation rule is added automatically.
Single Mode
Use single() when rows only need a value column:
KeyValue::make('aliases')
->single()
->valueHeader('Alias')
->addButtonLabel('Add alias');You may call keyed(), single(), or mode('keyed'|'single') when the storage shape is selected dynamically.
This stores:
["support", "billing"]For token suggestions, duplicate handling, delimiters, and free-form chip entry, use Combobox tokens instead.
Headers and Add Button
Customize the column headers and add button label:
KeyValue::make('headers')
->keyHeader('Header')
->valueHeader('Value')
->addButtonLabel('Add header');Passing null restores the translated component labels for these options.
Row Limits
Limit the number of rows:
KeyValue::make('metadata')
->minRows(1)
->maxRows(3);minRows() adds min: validation and prevents removing rows below the limit in the frontend component. maxRows() adds max: validation and hides the add button when the limit is reached.
Limits must be zero or greater, and the minimum must not exceed the maximum. A maximum of 0 prevents rows from being added. Passing null clears a configured limit.
Reordering
Rows are reorderable by default with move buttons. The controls are keyboard-focusable, so reordering is not drag-only:
KeyValue::make('metadata')->reorderable();
KeyValue::make('metadata')->reorderable(false);For keyed mode, row order is preserved when the object is rebuilt. Use stable string keys; duplicate keys are not represented distinctly in an object.
Defaults and Model Binding
Defaults use the same storage shape as submitted values:
KeyValue::make('metadata')->default([
'environment' => 'production',
'region' => 'eu-west',
]);
KeyValue::make('aliases')
->single()
->default(['support', 'billing']);Model binding uses the normal Field::getValue() path, so nested names work with data_get():
KeyValue::make('settings.metadata');A missing or null PHP value is normalized to an empty array. Arrays, Laravel collections, Arrayable values, and other traversable values are accepted; other PHP values become an empty array. A standalone frontend component with no value starts as {} in keyed mode or [] in single mode.
Nested Validation
Use valueRule() to append one rule or valueRules() to append an array of rules to every submitted value:
KeyValue::make('metadata')
->valueRules(['required', 'string']);This adds rules for metadata.*.
In keyed mode, keyRule() appends one key rule and keyRules() appends several:
KeyValue::make('metadata')
->keyRules(['regex:/^[a-z_]+$/'])
->valueRules(['required', 'string']);Key and value errors are reported at the submitted nested value path, such as metadata.environment.
Calling keyRule() or keyRules() after single(), or changing a field with key rules to single mode, throws an exception because single mode has no submitted keys.
KeyValue also supports the shared Form Class, Model Binding, Validation, Conditional Visibility, Authorization, and Styling APIs.
Using the Component Directly
You may render KeyValue with local state, or pass serialized field props during manual form rendering.
<script setup lang="ts">
import type { KeyValueFieldValue } from '@inertiaui/form-vue'
import { KeyValue } from '@inertiaui/form-vue/components'
import { ref } from 'vue'
const metadata = ref<KeyValueFieldValue>({
environment: 'production',
region: 'eu-west',
})
const locked = ref(false)
const metadataError = ref<string | null>(null)
const keyValuePartClasses = {
itemHeader: 'text-xs uppercase tracking-wide',
item: 'bg-white',
empty: 'text-zinc-500',
addButton: 'border-dashed',
}
</script>
<template>
<KeyValue
v-model="metadata"
id="deployment-metadata"
name="metadata"
label="Metadata"
help="Store environment metadata for this deployment."
mode="keyed"
key-header="Key"
value-header="Value"
add-button-label="Add metadata"
:min-rows="1"
:max-rows="5"
reorderable
required
:disabled="locked"
:invalid="Boolean(metadataError)"
:error="metadataError"
class="font-medium"
control-class="bg-white"
wrapper-class="max-w-2xl"
:part-classes="keyValuePartClasses"
/>
</template>import { useState } from 'react'
import type { KeyValueFieldValue } from '@inertiaui/form-react'
import { KeyValue } from '@inertiaui/form-react/components'
const keyValuePartClasses = {
itemHeader: 'text-xs uppercase tracking-wide',
item: 'bg-white',
empty: 'text-zinc-500',
addButton: 'border-dashed',
}
export default function MetadataEditor() {
const [metadata, setMetadata] = useState<KeyValueFieldValue>({
environment: 'production',
region: 'eu-west',
})
const [locked] = useState(false)
const [metadataError] = useState<string | null>(null)
return (
<KeyValue
value={metadata}
onValueChange={setMetadata}
id="deployment-metadata"
name="metadata"
label="Metadata"
help="Store environment metadata for this deployment."
mode="keyed"
keyHeader="Key"
valueHeader="Value"
addButtonLabel="Add metadata"
minRows={1}
maxRows={5}
reorderable
required
disabled={locked}
invalid={Boolean(metadataError)}
error={metadataError}
class="font-medium"
controlClass="bg-white"
wrapperClass="max-w-2xl"
partClasses={keyValuePartClasses}
/>
)
}KeyValue is a composite control, so native attributes and listeners do not target a single underlying native element. See Native Attributes and Events for native control forwarding.
Accessibility
Each row has labeled text inputs and keyboard-operable move and delete buttons. Pointer dragging is an additional reorder path, not the only one. Reorder results are announced through a live status region, focus follows keyboard moves, and duplicate keyed entries produce an inline alert. Disabled state applies to every row action and input.