Toggle Sidebar B
AppearanceLight & dark mode D

Custom Field

Renders a form field from a JSON definition instead of hand-written markup: one field with CustomFieldRenderer, a whole set with CustomFieldGroup. Covers 30+ types (text, choice, date and range pickers, sliders, rating, color, country, file) and ships the value helpers that go with them.

Single field

CustomFieldRenderer takes one definition and a v-model. Label, placeholder, help text and the required marker all come from the definition.

Dark previewReplay preview

Shown on your booth signage.

Value: empty

Field group

CustomFieldGroup renders a list of definitions and keeps one value map keyed by field ulid — the shape you post back to the server.

Dark previewReplay preview

Your details

We only use this to contact you about the event.

{}

Field types

A sample across the catalog: choice, date and range pickers, sliders, rating, color and country all render from the same definition shape.

Dark previewReplay preview
0 of 5 selected
––
––
––
––
00 - 100100

Validation errors

Pass the server's error bag. errorPrefix matches how your API keys them, and nested keys for range types (.start / .end) resolve automatically.

Dark previewReplay preview

Options and presets

Options accept { value, label } pairs, plain strings, or a translation map. settings.options_preset generates the list instead — years rolls over on its own.

Dark previewReplay preview

Localized labels

Labels, placeholders, help text and option labels may be { locale: string } maps; the locale prop picks the language, falling back to English.

Dark previewReplay preview

We pass this to the caterer.

Disabled and preview

disabled greys out the inputs; preview keeps a field interactive for a builder preview without wiring uploads.

Dark previewReplay preview

Value helpers

The runtime helpers exported alongside the components: seed a blank value, normalize an incoming definition, and format a stored answer for tables.

Dark previewReplay preview

normalizeField(field, "id")

{
  "key": "h-role",
  "type": "multi_select",
  "label": "Bidang minat",
  "options": [
    {
      "value": "design",
      "label": "Desain"
    },
    {
      "value": "dev",
      "label": "Pengembangan"
    }
  ],
  "required": true
}

defaultValueFor(field)[]

formatResponseValue(field, [ "design", "dev" ], "id")Desain, Pengembangan

API Reference

CustomFieldRenderer

PropTypeDefaultDescription
fieldCustomFieldShapeRequired. { ulid | id, type, label, placeholder, help_text, options, validation, settings }. Text values may be translation maps.
modelValueanynullThe answer. Shape follows the type: string, number, boolean, string[], or { start, end } for range types.
errorstringnullError message rendered under the field.
isFirstbooleanfalseMarks the first field of a form, used for autofocus-friendly spacing of section headings.
disabledbooleanfalseDisables the input.
previewbooleanfalseBuilder preview mode: keeps the field interactive but disables file uploads.
localestring"en"Locale used to resolve translation maps.
countriesarraynullOverride the country list for country fields. Defaults to the bundled list.
pinnedCountriesarray["Indonesia"]Countries pinned to the top of the country combobox.
uploadHandler(file, onProgress) => Promise<{ folder, name?, size? }>nullRequired for file fields: uploads one file and returns its stored reference.
revertHandler(folder) => Promise<void>nullCalled when the user removes a file that was just uploaded.
existingFilesarray[]Already-stored files for this field: [{ id, name, url, size }].
EventDescription
update:modelValueFires with the new answer. Enables v-model.
uploadingFires with a boolean while a file upload is in flight, so the submit button can wait.

CustomFieldGroup

PropTypeDefaultDescription
fieldsCustomFieldShape[][]The definitions to render, in order.
modelValueRecord<string, any>{}Value map keyed by valueKey. Blank values are seeded on mount from each type's default.
errorsRecord<string, string>{}Error bag keyed by `${errorPrefix}${fieldKey}`. Nested keys such as `.start`, `.end` and `.0` are matched too.
errorPrefixstring""Prefix your API uses, e.g. "responses." or "project_custom_fields.".
valueKey"ulid" | "key""ulid"Which field property keys the value map. Brand profiles key by key instead of ulid.
locale / disabled / preview / uploadHandler / revertHandlerForwarded to every CustomFieldRenderer.
existingFilesRecord<string, array>{}Stored files per field, keyed like modelValue.
EventDescription
update:modelValueFires with a new value map on every change. Enables v-model.
uploadingBubbles the uploading state of any file field in the group.

CustomFieldFileUpload

PropTypeDefaultDescription
fieldCustomFieldShapeRequired. Reads settings.multiple plus validation.max_files, max_file_size (KB) and allowed_file_types.
modelValuestring | string[]nullStored folder reference, or an array of them when settings.multiple is on.
disabled / existingFiles / uploadHandler / revertHandlerSame contract as CustomFieldRenderer. Rendered automatically for file fields; use it directly only outside a field definition.
EventDescription
update:modelValueFires with the stored reference(s) once an upload finishes.
uploadingFires with a boolean while uploading.

Helpers (core)

PropTypeDefaultDescription
normalizeField(field, locale)(CustomFieldShape, string) => NormalizedFieldCanonical form of a definition: resolves key, localized text, options (including presets) and the required flag. Every consumer should go through this instead of reading raw fields.
defaultValueFor(field)(field) => anyBlank value for a type: "" for text, [] for multi-value, false for checkbox/switch, null otherwise.
formatResponseValue(field, value, locale)(field, any, string) => stringHuman-readable rendering for tables and exports: option labels, "Yes"/"No", "start - end" for ranges, file basenames.
normalizeStoredValue(field, value)(field, any) => anyUnwraps the [value] scalar wrapping used by row-per-answer stores, while keeping range objects and multi-value arrays intact.
prefillValueFor(field, raw)(field, unknown) => anyCoerces a URL query value into the right shape, e.g. ?key=2026-01,2026-06 into { start, end }. Returns undefined when it cannot apply.
isEmptyValue(value)(any) => booleanEmpty check that understands arrays and { start, end } objects.
TYPE_META / OPTION_TYPES / MULTI_VALUE_TYPES / OBJECT_RANGE_TYPESconstPer-type capability flags and the type groupings, mirrored from the backend catalog.
MAX_UPLOAD_SIZE_KBnumber20480Hard ceiling for uploads (20 MB), matching the server limits. A field's max_file_size is clamped to it.

Accessibility

Keyboard shortcuts and ARIA behavior.

Shortcut Description
TabMoves between fields. Each field renders the same underlying component you would use by hand, so its own shortcuts apply.
SpaceToggles checkboxes and switches; selects the focused radio or rating star.
+ Moves within listboxes, comboboxes, radio groups and sliders.
EscCloses an open popover (date pickers, selects, colour picker).
  • Every field renders a <label> bound to its input id, so the label, help text and required marker are announced together.
  • Errors are rendered below the field and referenced by the input, including nested errors for range types.
  • Section fields are layout-only headings: they render no input and are stripped from submitted values.
  • Rich text fields load the editor lazily, so a form without one never pulls the editor bundle.