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.

import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import { DatePicker } from "@svar-ui/react-core";

registerEditorItem("datepicker", DatePicker);

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

<Editor items={items} />

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:

import { Editor } from "@svar-ui/react-editor";

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

<Editor items={items} values={values} />

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:

import { Editor } from "@svar-ui/react-editor";

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

<Editor items={items} values={values} />

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:

import { Editor } from "@svar-ui/react-editor";

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

<Editor items={items} values={values} />

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:

import { Editor } from "@svar-ui/react-editor";

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

<Editor items={items} values={values} />

Additional controls

Any control exported by @svar-ui/react-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.

import { Editor, registerEditorItem, Attachments } from "@svar-ui/react-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 }
];

<Editor items={items} />

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:

import { Editor, registerEditorItem, CodeMirror } from "@svar-ui/react-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" }
];

<Editor items={items} />

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.

import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import { ColorPicker } from "@svar-ui/react-core";

registerEditorItem("color", ColorPicker);

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

<Editor items={items} />

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:

import { useState } from "react";
import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import { Combo } from "@svar-ui/react-core";

registerEditorItem("combo", Combo);

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

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

const handleChange = ({ key, value }) => {
if (key === "country") {
const next = [...items];
next[1] = { ...next[1], disabled: false, options: citiesByCountry[value] };
setItems(next);
}
};

return <Editor items={items} onChange={handleChange} />;
}

Full prop reference: Combo

Comments

Comments comes from its own package, @svar-ui/react-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.

import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import { Comments } from "@svar-ui/react-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() }
]
};

<Editor items={items} values={data} />

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.

import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import { DatePicker } from "@svar-ui/react-core";

registerEditorItem("datepicker", DatePicker);

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

<Editor items={items} />

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.

import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import { MultiCombo } from "@svar-ui/react-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 }
];

<Editor items={items} />

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.

import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import { RichSelect } from "@svar-ui/react-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" }
]
}
];

<Editor items={items} />

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:

import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import { Slider } from "@svar-ui/react-core";

registerEditorItem("slider", Slider);

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

<Editor items={items} />

Full prop reference: Slider

TaskList

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

import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import { TaskList } from "@svar-ui/react-tasklist";

registerEditorItem("tasks", TaskList);

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

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

<Editor items={items} values={data} />

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

import { Editor, registerEditorItem, TinyMCE } from "@svar-ui/react-editor";

registerEditorItem("tinymce", TinyMCE);

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

<Editor items={items} />

Editor with a TinyMCE rich-text field

Custom Controls

Any React 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.jsx
function Rating({ value = 0, onChange }) {
return (
<div className="rating">
{[1, 2, 3, 4, 5].map(star => (
<button
key={star}
className={star <= value ? "filled" : ""}
onClick={() => onChange({ value: star })}
>

</button>
))}
</div>
);
}

export default Rating;

Register and use it exactly like any other control:

import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import Rating from "./Rating.jsx";

registerEditorItem("rating", Rating);

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

<Editor items={items} values={values} />

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.jsx
import { DatePicker, Switch } from "@svar-ui/react-core";

function EventDates({ value, onChange }) {
const update = part => onChange({ value: { ...value, ...part } });

return (
<>
<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 })} />
</>
);
}

export default EventDates;

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:

import { Editor, registerEditorItem } from "@svar-ui/react-editor";
import EventDates from "./EventDates.jsx";

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
};

<Editor items={items} values={values} />