Skip to main content
The V2 data form schema is built around three core types: UINode (the building block for every element), EventConfig (the handler attached to component events), and ForConfig (the iterator for rendering lists). This page documents every field on these types with descriptions, types, and examples. Understanding the schema structure is essential for building V2 forms. Each node in the nodes array describes a single component instance, including its props, bindings, event handlers, conditional rendering, and children. The renderer walks this tree top-down, resolving bindings and evaluating conditions at each level. For a hands-on introduction, start with the Getting Started guide.

DataFormV2

The top-level form object extends the standard DataForm model with V2-specific fields.
FieldTypeRequiredDescription
version"1" | "2"NoSet to "2" to enable V2 rendering. If omitted, the presence of a non-empty nodes array also triggers V2 mode.
nodesUINode[]YesThe root-level component tree.
scriptsRecord<string, string>NoNamed script registry. Keys are script names, values are JavaScript function bodies.
scriptModulesRecord<string, ScriptModule>NoNamed script modules with metadata, input declarations, and debounce settings.
bridgeBridgeConfigNoConfiguration for the kodexa.* bridge API.
{
  "version": "2",
  "nodes": [ ... ],
  "scripts": {
    "formatTotal": "function(ctx) { return '$' + Number(ctx.value).toFixed(2); }"
  },
  "scriptModules": {
    "validateRow": {
      "source": "function(ctx) { return ctx.amount > 0; }",
      "description": "Validates that amount is positive",
      "inputs": { "amount": "number" },
      "returns": "boolean",
      "debounce": 300
    }
  },
  "bridge": {
    "permissions": ["data:read", "data:write", "navigation"],
    "maxExecutionMs": 2000
  }
}

UINode

A UINode represents a single component in the form tree. It is the fundamental building block of V2 forms.
FieldTypeRequiredDescription
componentstringYesThe component to render, in namespace:type format (e.g., "card:cardPanel") or just the type name (e.g., "cardPanel").
propsRecord<string, any>NoStatic properties passed directly to the component. These are not evaluated as expressions.
bindingsRecord<string, string>NoDynamic properties. Each value is a JavaScript expression evaluated against the data context (ctx). Results are merged with props, with bindings taking precedence.
computedRecord<string, string>NoProperties derived from named scripts. Each value is a script name from the scripts registry. The script is called with the current data context and its return value becomes the prop.
eventsRecord<string, EventConfig | EventConfig[]>NoEvent handlers. Keys are event names (e.g., "click", "change"). Values are a single EventConfig or an array of them.
childrenUINode[]NoChild nodes rendered inside this component’s default slot.
slotsRecord<string, UINode[]>NoNamed slot content. Keys are slot names, values are arrays of UINode to render in that slot.
ifstringNoConditional rendering expression. The node is only rendered when this expression evaluates to a truthy value.
showstringNoVisibility expression. Unlike if, the component is still mounted but hidden when the expression is falsy.
forForConfigNoLoop configuration. Renders this node once for each item in the source collection.
keystringNoUnique key for list rendering. Helps the renderer efficiently update the DOM when items change.
classstring | Record<string, string>NoCSS classes. Can be a static string or an object mapping class names to binding expressions.
styleRecord<string, any> | stringNoInline styles as a CSS string or an object of style properties.
refstringNoA reference name for this node, used with kodexa.form.getNodeRef() to programmatically interact with it.
metaNodeMetaNoDesign-time metadata that does not affect rendering.

Full UINode Example

{
  "component": "card:cardPanel",
  "props": {
    "title": "Line Items",
    "groupTaxon": "lineItem",
    "useTabs": true
  },
  "bindings": {
    "subtitle": "'Total: ' + ctx.dataObjects?.length + ' items'"
  },
  "computed": {
    "backgroundColor": "deriveColor"
  },
  "events": {
    "click": {
      "type": "emit",
      "target": "panel-clicked"
    }
  },
  "if": "ctx.dataObjects?.length > 0",
  "ref": "lineItemPanel",
  "key": "line-items",
  "class": "mt-4",
  "children": [
    {
      "component": "card:dataAttributeEditor",
      "props": { "taxon": "lineItem/description" }
    }
  ]
}

NodeMeta

Design-time metadata attached to a node. This information is used by form editors and does not affect runtime rendering.
FieldTypeRequiredDescription
labelstringNoHuman-readable label for the node, shown in design tools.
descriptionstringNoLonger description of the node’s purpose.
designOnlybooleanNoWhen true, the node is only visible in the form designer, not at runtime.
categorystringNoCategory for grouping in the designer palette.
{
  "meta": {
    "label": "Invoice Header Panel",
    "description": "Displays the invoice number and date fields",
    "category": "layout"
  }
}

EventConfig

An EventConfig defines what happens when a component emits an event. Multiple configs can be attached to the same event using an array.
FieldTypeRequiredDescription
type"script" | "scriptRef" | "emit" | "store-action" | "bus-event"YesThe handler type. See Event Handling for details on each.
targetstringYesThe target depends on type: a script expression, a script name, an event name, an action name, or a bus event name.
paramsRecord<string, any>NoAdditional parameters passed to the handler.
conditionstringNoA JavaScript expression that must evaluate to truthy for the handler to execute.
debouncenumberNoMilliseconds to debounce the handler. The handler will not fire until this many milliseconds have passed since the last trigger.

EventConfig Examples

Inline script:
{
  "type": "script",
  "target": "kodexa.log.debug('clicked', ctx.$item?.uuid)"
}
Named script reference:
{
  "type": "scriptRef",
  "target": "validateAndSave",
  "params": { "strict": true }
}
Emit event to parent:
{
  "type": "emit",
  "target": "row-selected",
  "condition": "ctx.$item?.status !== 'locked'"
}
Debounced handler:
{
  "type": "script",
  "target": "kodexa.data.setAttribute(ctx.$item.uuid, 'search', ctx.query)",
  "debounce": 300
}

ForConfig

A ForConfig turns a single UINode into a list renderer. The node is rendered once for each item in the source collection, with loop variables injected into the data context.
FieldTypeRequiredDescription
sourcestringYesA JavaScript expression that evaluates to an array (e.g., "ctx.dataObjects").
itemAsstringYesThe variable name for the current item, injected into the data context (e.g., "$item").
indexAsstringNoThe variable name for the current index (e.g., "$index"). Defaults to "$index" if omitted.
keystringYesAn expression evaluated per item to produce a unique key (e.g., "$item.uuid").

ForConfig Example

{
  "component": "card:label",
  "for": {
    "source": "ctx.dataObjects",
    "itemAs": "$item",
    "indexAs": "$index",
    "key": "$item.uuid"
  },
  "bindings": {
    "label": "$item.path + ' (#' + ($index + 1) + ')'"
  }
}
This renders one label per data object, showing its path and 1-based index.

BridgeConfig

Controls the kodexa.* bridge API available to scripts.
FieldTypeRequiredDescription
permissionsBridgePermission[]NoList of permissions to grant. Scripts that call methods requiring a permission not in this list will throw an error.
apiBaseUrlstringNoBase URL for kodexa.http methods. Defaults to "/api".
maxExecutionMsnumberNoMaximum execution time in milliseconds for any single script evaluation. Defaults to 1000.

BridgePermission Values

PermissionGrants Access To
data:readkodexa.data.getDataObjects, getDataObject, getAttributes, getAttribute, getTagMetadata, getTaxonomies
data:writekodexa.data.setAttribute, addDataObject, deleteDataObject
navigationkodexa.navigation.focusAttribute, scrollToNode, switchView
formStatekodexa.form.get, set, getNodeRef
http:getkodexa.http.get
http:postkodexa.http.post
{
  "bridge": {
    "permissions": ["data:read", "data:write", "navigation", "formState"],
    "apiBaseUrl": "/api",
    "maxExecutionMs": 2000
  }
}

ScriptModule

A ScriptModule provides richer metadata for named scripts, beyond what the flat scripts registry offers.
FieldTypeRequiredDescription
sourcestringYesThe JavaScript function body.
descriptionstringNoHuman-readable description of what the script does.
inputsRecord<string, string>NoDeclares expected input types. Keys are parameter names, values are type descriptions.
returnsstringNoDescribes the return type.
debouncenumberNoDefault debounce in milliseconds when this script is invoked.
{
  "scriptModules": {
    "computeTotal": {
      "source": "function(ctx) { return ctx.items.reduce((sum, i) => sum + i.amount, 0); }",
      "description": "Sums all line item amounts",
      "inputs": { "items": "DataObject[]" },
      "returns": "number",
      "debounce": 200
    }
  }
}

Common Patterns

Conditional Rendering

Use if to conditionally include a node in the tree:
{
  "component": "card:label",
  "if": "ctx.dataObjects?.some(o => o.path === 'invoice')",
  "props": { "label": "Invoice detected" }
}

Visibility Toggle

Use show to keep a node mounted but hidden:
{
  "component": "card:cardPanel",
  "show": "ctx.$item?.status !== 'draft'",
  "props": { "title": "Published Content" }
}

Named Slots

Use slots to place content in specific named slots of a component:
{
  "component": "card:tabs",
  "slots": {
    "tab-1": [
      { "component": "card:label", "props": { "label": "First Tab" } }
    ],
    "tab-2": [
      { "component": "card:grid", "props": { "dataStoreRef": "items" } }
    ]
  }
}

Next Steps