Skip to content

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

Example
KeyValue
KeyValue

php
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:

json
{
    "environment": "production",
    "region": "eu-west"
}

The array validation rule is added automatically.

Single Mode

Use single() when rows only need a value column:

Example
Alias
Alias

php
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:

json
["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:

php
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:

Example
KeyValue
KeyValue
KeyValue

php
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:

php
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:

php
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():

php
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:

php
KeyValue::make('metadata')
    ->valueRules(['required', 'string']);

This adds rules for metadata.*.

In keyed mode, keyRule() appends one key rule and keyRules() appends several:

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

vue
<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>
tsx
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.