Skip to main content

Input Controls

This guide covers which controls an item can render out of the box, and how to bring in any other control for the rest - from SVAR Core, from the editor package itself, from a standalone widget package, or a fully custom component you write.

Registering a control

Every non-built-in control follows the same pattern: import the component and register it under a string with registerEditorItem, then reference that string from an item's comp. Registration is global and only needs to run once before the editor renders.

<script setup>
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import { DatePicker } from "@svar-ui/vue-core";

registerEditorItem("datepicker", DatePicker);

const items = [
{ comp: "datepicker", key: "deadline", label: "Deadline" }
];
</script>

<template>
<Editor :items="items" />
</template>

Built-in controls

These need no registration:

compRenders
checkboxBoolean toggle
readonlyStatic display-only value
textSingle-line text input (also the default when comp is omitted)
textareaMulti-line text input

One more built-in comp isn't an input at all: "section" renders a collapsible group header, see Layout.

checkbox

A checkbox binds a boolean value to a toggle:

<script setup>
import { Editor } from "@svar-ui/vue-editor";

const items = [{ comp: "checkbox", key: "admin", label: "Is Admin" }];
const values = { admin: true };
</script>

<template>
<Editor :items="items" :values="values" />
</template>

readonly

A readonly item shows its value as text instead of an input, while the rest of the form stays editable. Booleans print as Yes/No, options resolve to their matching label, and dates are formatted:

<script setup>
import { Editor } from "@svar-ui/vue-editor";

const items = [{ comp: "readonly", key: "name", label: "Name" }];
const values = { name: "John Doe" };
</script>

<template>
<Editor :items="items" :values="values" />
</template>

See Fields for the details, and Display modes for making the whole form view-only.

text

A text field binds a string value to a single-line input. It is also what an item renders when comp is omitted:

<script setup>
import { Editor } from "@svar-ui/vue-editor";

const items = [{ comp: "text", key: "name", label: "Name" }];
const values = { name: "John Doe" };
</script>

<template>
<Editor :items="items" :values="values" />
</template>

textarea

A textarea binds a string value to a multi-line input. Extra props are forwarded to the control, so placeholder and the widget's own sizing props can be set right on the item:

<script setup>
import { Editor } from "@svar-ui/vue-editor";

const items = [
{ comp: "textarea", key: "descr", label: "Description", placeholder: "Add description" }
];
const values = { descr: "Team lead on the platform group" };
</script>

<template>
<Editor :items="items" :values="values" />
</template>

Additional controls

Any control exported by @svar-ui/vue-core, by the editor package itself, or by a standalone widget package, can be registered as described above. The controls below are the ones used most often - each links to its full prop reference.

Attachments

Bundled with the editor, Attachments renders a file list with upload/delete controls. Pass uploadURL - a function that receives the selected file and returns a promise resolving to the stored record - and an onaction callback to observe add/delete events. Its labels (Add file, Uploading..., Upload failed, Delete file) come from the editor locale.

<script setup>
import { Editor, registerEditorItem, Attachments } from "@svar-ui/vue-editor";

registerEditorItem("files", Attachments);
registerEditorItem("files", Attachments, { readonly: true });

const uploadURL = file =>
fetch("/upload", { method: "POST", body: file }).then(r => r.json());

const items = [
{ comp: "files", key: "files", label: "Attachments", uploadURL, onaction: console.log }
];
</script>

<template>
<Editor :items="items" />
</template>

Editor with an attachments list, one upload in progress

CodeMirror

The editor package bundles CodeMirror, a syntax-highlighted code editor, directly - no extra package to install. It takes fill: true to stretch across the available height (see full-height single field), and language to set highlighting:

<script setup>
import { Editor, registerEditorItem, CodeMirror } from "@svar-ui/vue-editor";

registerEditorItem("code-mirror", CodeMirror);
// register a handler for readonly mode too, otherwise it falls back to a plain text display
registerEditorItem("code-mirror", CodeMirror, { readonly: true });

const items = [
{ comp: "code-mirror", key: "content", label: "", fill: true, language: "javascript" }
];
</script>

<template>
<Editor :items="items" />
</template>

Editor with a syntax-highlighted CodeMirror code field

ColorPicker

A swatch-and-picker control bound directly to a hex color string - no options needed. Same clear: true pattern as DatePicker to let users unset the value.

<script setup>
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import { ColorPicker } from "@svar-ui/vue-core";

registerEditorItem("color", ColorPicker);

const items = [{ comp: "color", key: "color", label: "Color" }];
</script>

<template>
<Editor :items="items" />
</template>

Full prop reference: ColorPicker

Combo

A single-select dropdown driven by a plain options array of { id, label } pairs. Because an item is just a plain object, you can replace its options at runtime - the pattern below rebuilds the city list whenever country changes, disabling city until a country is picked:

<script setup>
import { ref } from "vue";
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import { Combo } from "@svar-ui/vue-core";

registerEditorItem("combo", Combo);

const citiesByCountry = {
france: [{ id: "paris", label: "Paris" }, { id: "lyon", label: "Lyon" }]
};

const items = ref([
{
comp: "combo",
key: "country",
label: "Country",
options: [{ id: "france", label: "France" }]
},
{ comp: "combo", key: "city", label: "City", disabled: true }
]);

function handleChange({ key, value }) {
if (key === "country") {
items.value[1] = { ...items.value[1], disabled: false, options: citiesByCountry[value] };
}
}
</script>

<template>
<Editor :items="items" :onchange="handleChange" />
</template>

Full prop reference: Combo

Comments

Comments comes from its own package, @svar-ui/vue-comments, and manages an array of comment entries rather than a single value. users lists the people who can be shown as authors, and activeUser is the id of the person posting from this editor instance.

<script setup>
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import { Comments } from "@svar-ui/vue-comments";

registerEditorItem("comments", Comments);

const users = [{ id: 1, name: "John Doe", avatar: "https://via.placeholder.com/150" }];

const items = [
{ comp: "comments", key: "comments", label: "Comments", users, activeUser: 1 }
];

const data = {
comments: [
{ id: 1, user: 1, content: "Greetings, fellow colleagues.", date: new Date() }
]
};
</script>

<template>
<Editor :items="items" :values="data" />
</template>

Full prop reference: Comments

DatePicker

A calendar-backed date input. Add clear: true for a button that unsets the value, and format to override the date string pattern for that field - it otherwise falls back to formats.dateFormat from the active locale.

<script setup>
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import { DatePicker } from "@svar-ui/vue-core";

registerEditorItem("datepicker", DatePicker);

const items = [
{ comp: "datepicker", key: "deadline", label: "Deadline", clear: true }
];
</script>

<template>
<Editor :items="items" />
</template>

Full prop reference: DatePicker

MultiCombo

The multi-select counterpart to Combo - same options shape, but the bound value is an array of ids. Set checkboxes: true to show a checkbox next to each option in the dropdown list.

<script setup>
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import { MultiCombo } from "@svar-ui/vue-core";

registerEditorItem("multiselect", MultiCombo);

const users = [
{ id: 1, label: "Sarah Smith" },
{ id: 2, label: "Diego Redmoor" }
];

const items = [
{ comp: "multiselect", key: "users", label: "Users", checkboxes: true, options: users }
];
</script>

<template>
<Editor :items="items" />
</template>

Full prop reference: MultiCombo

RichSelect

A single-select dropdown for picking from a predefined list. Give it an options array of { id, label } pairs to resolve a stored id to a display label; add clear: true to let users unset the value once one is picked.

<script setup>
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import { RichSelect } from "@svar-ui/vue-core";

registerEditorItem("select", RichSelect);

const items = [
{
comp: "select",
key: "priority",
label: "Priority",
clear: true,
options: [
{ id: 1, label: "High" },
{ id: 2, label: "Medium" },
{ id: 3, label: "Low" }
]
}
];
</script>

<template>
<Editor :items="items" />
</template>

Full prop reference: RichSelect

Slider

A numeric range input. min, max, and step shape the range - for a 0-1 progress value stepped by 10%, for example:

<script setup>
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import { Slider } from "@svar-ui/vue-core";

registerEditorItem("slider", Slider);

const items = [
{ comp: "slider", key: "progress", label: "Progress", min: 0, max: 1, step: 0.1 }
];
</script>

<template>
<Editor :items="items" />
</template>

Full prop reference: Slider

TaskList

TaskList comes from @svar-ui/vue-tasklist and also manages array-shaped data - a checklist of { id, content, status } entries, where status is 0 for open and 1 for done.

<script setup>
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import { TaskList } from "@svar-ui/vue-tasklist";

registerEditorItem("tasks", TaskList);

const items = [{ comp: "tasks", key: "task", label: "Task" }];

const data = {
task: [{ id: 1, content: "Task 1", status: 1 }]
};
</script>

<template>
<Editor :items="items" :values="data" />
</template>

Full prop reference: Tasklist

Task list rendered inside the editor

TinyMCE

Also bundled with the editor, TinyMCE renders a WYSIWYG rich text field. Same fill: true pattern as CodeMirror to stretch it across the available height (see full-height single field):

<script setup>
import { Editor, registerEditorItem, TinyMCE } from "@svar-ui/vue-editor";

registerEditorItem("tinymce", TinyMCE);

const items = [
{ comp: "tinymce", key: "content", label: "Content", fill: true }
];
</script>

<template>
<Editor :items="items" />
</template>

Editor with a TinyMCE rich-text field

Custom Controls

Any Vue component can become an editor control - built-in and SVAR Core controls all implement the same minimal interface: receive the field's current value through a value prop, and report changes by calling an onchange prop with the new value. A custom five-star rating control, for example:

<!-- Rating.vue -->
<script setup>
defineProps({
value: { default: 0 },
onchange: { type: Function }
});
</script>

<template>
<div class="rating">
<button
v-for="star in [1, 2, 3, 4, 5]"
:key="star"
:class="{ filled: star <= value }"
:onclick="() => onchange?.({ value: star })"
>

</button>
</div>
</template>

Register and use it exactly like any other control:

<script setup>
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import Rating from "./Rating.vue";

registerEditorItem("rating", Rating);

const items = [{ comp: "rating", key: "rating", label: "Rating" }];
const values = { rating: 3 };
</script>

<template>
<Editor :items="items" :values="values" />
</template>

Multi-field Controls

A control isn't limited to editing a single value. Add a keys array to an item and the editor collects those fields from values into one object, passing it to your component as value; on onchange, it spreads the updated object back across the same keys. This is how you wire in a component that edits several related fields together - a date range with an all-day toggle, for example:

<!-- EventDates.vue -->
<script setup>
import { DatePicker, Switch } from "@svar-ui/vue-core";

const props = defineProps({
value: {},
onchange: { type: Function }
});
const update = part => props.onchange?.({ value: { ...props.value, ...part } });
</script>

<template>
<DatePicker :value="value.start" :onchange="ev => update({ start: ev.value })" />
<DatePicker :value="value.end" :onchange="ev => update({ end: ev.value })" />
<Switch :value="value.isFullDay" :onchange="ev => update({ isFullDay: ev.value })" />
</template>

value here is not the raw field value but an object assembled from keys, so the component receives { start, end, isFullDay } and updates one or more of those fields at a time:

<script setup>
import { Editor, registerEditorItem } from "@svar-ui/vue-editor";
import EventDates from "./EventDates.vue";

registerEditorItem("event-dates", EventDates);

const items = [
{ comp: "text", key: "name", label: "Name" },
{
comp: "event-dates",
key: "dates",
keys: ["start", "end", "isFullDay"],
label: "Event dates",
validation: v => !v.start || !v.end || v.end >= v.start,
validationMessage: "End date must be after the start date"
}
];

const values = {
name: "Project kickoff",
start: new Date(2026, 5, 10, 9),
end: new Date(2026, 5, 10, 11),
isFullDay: false
};
</script>

<template>
<Editor :items="items" :values="values" />
</template>