Component

JSON list input

<DomJsonListInput>

A schema-aware editor for arrays of objects. Edit rows through form controls, or toggle to raw JSON for bulk changes.

Playground

Try every prop live

JSON list input playground

Use the fields mode for structured editing, then switch to JSON when copy, paste, or bulk edits are quicker.

Row 1
Row 2

Schema-aware rows with a raw JSON toggle.

Playground.vuevue
<script setup>
import { reactive } from 'vue';
import { DomJsonListInput } from '@getdom/studio/vue';

const data = reactive({
	  "modelValue": [
	    {
	      "label": "Profile",
	      "value": "profile",
	      "shortcut": "Cmd+P"
	    },
	    {
	      "label": "Settings",
	      "value": "settings",
	      "shortcut": "Cmd+,"
	    }
	  ],
	  "id": "",
	  "name": "",
	  "label": "Menu items",
	  "description": "Schema-aware rows with a raw JSON toggle.",
	  "placeholder": "",
	  "required": false,
	  "disabled": false,
	  "readOnly": false,
	  "invalid": false,
	  "errors": {},
	  "visible": true,
	  "validators": [],
	  "validateOnBlur": true,
	  "chrome": "field",
	  "addLabel": "+ Add item",
	  "schema": {
	    "type": "array",
	    "label": "Menu items",
	    "items": {
	      "type": "DomForm",
	      "properties": {
	        "label": {
	          "type": "string",
	          "label": "Label",
	          "placeholder": "Profile"
	        },
	        "value": {
	          "type": "string",
	          "label": "Value",
	          "placeholder": "profile"
	        },
	        "shortcut": {
	          "type": "string",
	          "label": "Shortcut",
	          "placeholder": "Cmd+P"
	        }
	      }
	    }
	  },
	  "jsonToggle": true,
	  "compact": false
	});
</script>

<template>
	<DomJsonListInput
		v-bind="data"
		@update:modelValue="data.modelValue = $event"
	/>
</template>

Demo

Label/value options

A label/value picker list is just DomJsonListInput with a two-field schema.

Row 1
Row 2
Row 3

A simple label/value list is just a schema with two fields.

Bound elsewhere

[
  {
    "label": "Apple",
    "value": "apple"
  },
  {
    "label": "Banana",
    "value": "banana"
  },
  {
    "label": "Cherry",
    "value": "cherry"
  }
]
LabelValueOptions.vuevue
<script setup>
import { ref } from 'vue';
import { DomDropdown, DomJsonListInput } from '../../../lib/vue';

const items = ref([
	{ label: 'Apple', value: 'apple' },
	{ label: 'Banana', value: 'banana' },
	{ label: 'Cherry', value: 'cherry' },
]);

const schema = {
	type: 'array',
	label: 'Dropdown items',
	items: {
		type: 'DomForm',
		properties: {
			label: {
				type: 'string',
				label: 'Label',
				placeholder: 'Item label',
			},
			value: {
				type: 'string',
				label: 'Value',
				placeholder: 'item-value',
			},
		},
	},
};
</script>

<template>
	<div class="grid w-full max-w-4xl gap-5 lg:grid-cols-[minmax(0,1fr)_18rem]">
		<DomJsonListInput
			v-model="items"
			label="Dropdown items"
			description="A simple label/value list is just a schema with two fields."
			add-label="+ Add fruit"
			:schema="schema"
		/>
		<div>
			<p class="mb-2 text-xs font-medium uppercase tracking-wider text-muted-fg">Bound elsewhere</p>
			<DomDropdown :items="items" label="Pick a fruit" />
			<pre class="mt-3 overflow-auto rounded-xl border border-border bg-secondary/50 p-3 text-xs text-muted-fg">{{ items }}</pre>
		</div>
	</div>
</template>

Demo

Schema-driven rows

The standardized form schema controls which fields appear, their labels, placeholders, input types, options, and defaults for new rows.

Row 1
Row 2

Use the field editor for day-to-day edits, or switch to JSON for bulk changes.

[
  {
    "id": "toast-1",
    "title": "Saved",
    "tone": "success",
    "duration": 3200
  },
  {
    "id": "toast-2",
    "title": "Could not publish",
    "tone": "danger",
    "duration": 0
  }
]
SchemaRows.vuevue
<script setup>
import { ref } from 'vue';
import { DomJsonListInput } from '../../../lib/vue';

const rows = ref([
	{ id: 'toast-1', title: 'Saved', tone: 'success', duration: 3200 },
	{ id: 'toast-2', title: 'Could not publish', tone: 'danger', duration: 0 },
]);

const schema = {
	type: 'array',
	label: 'Toast rows',
	items: {
		type: 'DomForm',
		properties: {
			id: {
				type: 'string',
				label: 'ID',
				placeholder: 'toast-id',
			},
			title: {
				type: 'string',
				label: 'Title',
				placeholder: 'Successfully saved',
			},
			tone: {
				type: 'string',
				component: 'DomSelectInput',
				label: 'Tone',
				options: [
					'default',
					'success',
					'danger',
					'warning',
				],
			},
			duration: {
				type: 'number',
				label: 'Duration',
				default: 3600,
			},
		},
	},
};
</script>

<template>
	<div class="grid w-full max-w-4xl gap-5 lg:grid-cols-[minmax(0,1fr)_18rem]">
		<DomJsonListInput
			v-model="rows"
			label="Toast rows"
			description="Use the field editor for day-to-day edits, or switch to JSON for bulk changes."
			add-label="+ Add toast"
			:schema="schema"
		/>
		<pre class="max-h-96 overflow-auto rounded-xl border border-border bg-secondary/50 p-3 text-xs text-muted-fg">{{ rows }}</pre>
	</div>
</template>

Demo

Compact inspector mode

Compact mode keeps the editor useful inside a Studio-style inspector without removing the raw JSON toggle.

Row 1

Meta

Row 2

Meta

Compact mode is intended for inspectors and narrow panels.

CompactRows.vuevue
<script setup>
import { ref } from 'vue';
import { DomJsonListInput } from '../../../lib/vue';

const people = ref([
	{
		value: 'person-1',
		label: 'Maya Patel',
		role: 'Product lead',
		avatar: 'https://i.pravatar.cc/96?img=47',
		meta: { team: 'Design systems', timezone: 'GMT' },
	},
	{
		value: 'person-2',
		label: 'Noah Hughes',
		role: 'Frontend engineer',
		avatar: 'https://i.pravatar.cc/96?img=12',
		meta: { team: 'Studio', timezone: 'CET' },
	},
]);

const schema = {
	type: 'array',
	label: 'People',
	items: {
		type: 'DomForm',
		properties: {
			value: {
				type: 'string',
				label: 'Value',
				placeholder: 'person-id',
			},
			label: {
				type: 'string',
				label: 'Name',
				placeholder: 'Person name',
			},
			role: {
				type: 'string',
				label: 'Role',
				placeholder: 'Role or title',
			},
			avatar: {
				type: 'url',
				label: 'Avatar URL',
				placeholder: 'https://...',
			},
			meta: {
				type: 'DomForm',
				label: 'Meta',
				properties: {
					team: {
						type: 'string',
						label: 'Team',
					},
					timezone: {
						type: 'string',
						label: 'Timezone',
					},
				},
			},
		},
	},
};
</script>

<template>
	<div class="w-full max-w-md rounded-2xl border border-border skin-raised p-4 text-fg">
		<DomJsonListInput
			v-model="people"
			label="People"
			description="Compact mode is intended for inspectors and narrow panels."
			add-label="+ Add person"
			:schema="schema"
			compact
		/>
	</div>
</template>

Reference

Props

Control props

NameTypeTSDefaultDescription
modelValuearrayArray<unknown>[]
addLabelstringstring'+ Add row'Button label for adding a row.
schemaarray | objectArray<unknown>Standardized form definition used by the visual editor. Legacy flat row schema arrays are still supported.
jsonTogglebooleanbooleantrueShow the visual/raw JSON mode switch.
compactbooleanbooleanfalseReduce vertical spacing for inspectors and narrow tool panels.

Field props

NameTypeTSDefaultDescription
idstringstring''Optional ID override. By default parent forms derive the input ID from the field path using underscores.
namestringstring''Local field name. Parent forms derive the full field path and native HTML name from the form hierarchy.
labelstringstring''Visible field label.
descriptionstringstring''Optional helper copy below the field.
placeholderstringstring''Placeholder shown when the control is empty.
requiredbooleanbooleanfalseMark the field as required.
disabledbooleanbooleanfalseDisable field interaction.
readOnlybooleanbooleanfalseShow the value but prevent editing.
invalidbooleanbooleanfalseMark the field invalid.
errors
[
	{
		name: "Validation name",
		message: "Error message",
	}
]
array | object | stringArray<ErrorsItem
type ErrorsItem = {
	name?: string; // Name
	message?: string; // Message
};
>
{}Validation errors for this field.
visiblebooleanbooleantrueShow or hide the field.
validatorsarrayArray<unknown>[]Validators attached to this field. Use functions in Vue code, or serializable records such as { name: "minLength", props: { min: 2 } } in generated schemas.
validateOnBlurbooleanbooleantrueRun validators when the field loses focus.
chrome'field' | falsestring'field'Render default field chrome, or false to render only the control while keeping form state wiring.

Auto-generated from JSON list input.props and inline _edit hints.

Events

NamePayloadDescription
@update:modelValueArray<object>Fired when rows are added, removed, reordered, edited, or replaced from valid raw JSON.
@focus
@blur

Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.