Skip to main content
The component registry is the lookup system that maps a component string in a UINode to an actual Vue component implementation. Every component available in V2 forms is registered in the global registry with a namespace, type, category, and implementation. When the SchemaNode renderer encounters a component value, it resolves it through the registry to find the matching component. This page documents how the registry works, lists all available built-in components, and explains how custom components can be registered. Understanding the registry helps you choose the right component for your use case and ensures that component values in your form schema are valid. For details on how components receive their props and events at runtime, see the Schema Structure and Data Binding guides.

How Components Are Registered

Components are registered with a namespace and type, forming a key in the format namespace:type. The global registry is initialized at startup by scanning all card Vue files and registering them under the card namespace.
// Registration format
registry.register("card", {
  type: "label",
  name: "Label",
  category: "card",
  implementation: () => LabelComponent,
  options: [],
  supportsChildren: false,
});

Resolving Components

When a UINode specifies a component, the renderer resolves it through the registry:
  1. Full key: "card:label" — matches directly against the namespace:type key.
  2. Alias: "label" — the registry maintains an alias map from type name to full key, so short names also work.
Both of these are equivalent:
{ "component": "card:label" }
{ "component": "label" }
Using the full namespace:type format is recommended for clarity and to avoid ambiguity if multiple namespaces register components with the same type name.

ComponentDefinition Interface

Each registered component has the following metadata:
FieldTypeDescription
typestringThe component type identifier (e.g., "label", "cardPanel")
namestringHuman-readable display name
categorystringOne of "card", "option", "layout", "kendo", "custom"
implementationfunctionFactory function returning the Vue component
optionsany[]Configuration options accepted by the component
supportsChildrenbooleanWhether the component can contain child nodes
defaultsRecord<string, any>Default prop values
emitsRecord<string, object>Events the component emits, with payload descriptions
slotsRecord<string, object>Named slots the component provides

Available Card Components

All built-in card components are registered under the card namespace. They are automatically discovered from Vue files following the naming convention kodexa-form-card-*.vue.

Layout Cards

Layout cards organize and structure form content. They typically support children.
ComponentTypeSupports ChildrenDescription
Card GroupcardGroupYesContainer for visually grouping related cards together. Renders children in a shared visual boundary.
Card PanelcardPanelYesFull-featured panel with title bar, tabs, exception handling, and collapsible content. The most commonly used container component.
TabstabsYesPresents child cards in a tabbed interface. Each child becomes a separate tab.
Horizontal LinehorizonalLineNoVisual separator line for organizing content within a form.
The horizontal line component type is horizonalLine (note the spelling). This matches the underlying Vue file name.

Display Cards

Display cards show data in read-only or summary formats.
ComponentTypeSupports ChildrenDescription
LabellabelNoDisplays static text or dynamic values. Commonly used for headings, field labels, and computed display values.
Single TaxonsingleTaxonNoDisplays a single taxonomy value with formatting appropriate to the taxon type.
Coming SooncomingSoonNoPlaceholder component for features under development.

Data Grid Cards

Grid cards display tabular data with sorting, filtering, and interaction capabilities.
ComponentTypeSupports ChildrenDescription
GridgridNoStandard data grid with sorting and filtering. Displays data from a configured source.
Data Store GriddataStoreGridNoGrid connected directly to a Kodexa data store. Supports pagination and server-side operations.
Taxon GridtaxonGridNoGrid specialized for displaying taxonomy-structured data with columns derived from taxon paths.
Workspace Data GridworkspaceDataGridNoGrid that displays data across workspaces, supporting cross-workspace queries.
Transposed GridtransposedGridNoGrid with rows and columns swapped, useful for comparison views where attributes become rows.
Transposed Grid RolluptransposedGridRollupNoTransposed grid with hierarchical rollup calculations. Supports drag-and-drop reordering and aggregation.
Transposed Grid Aggregated CelltransposedGridAggregatedCellNoSpecialized cell component for aggregated values within transposed grid rollups.
Transposed Grid Data Property RolluptransposedGridDataPropertyRollupNoRollup component that aggregates based on data object properties rather than attribute values.

Tree View Cards

Tree cards visualize hierarchical data structures.
ComponentTypeSupports ChildrenDescription
Data Objects TreedataObjectsTreeNoTree view of nested data objects, showing the parent-child hierarchy.
Workspace Data Tree ViewworkspaceDataTreeViewNoTree view of the workspace data structure, including document families and their objects.

Editor Cards

Editor cards provide interactive components for data modification.
ComponentTypeSupports ChildrenDescription
Data Attribute EditordataAttributeEditorNoEdit individual data attributes with type-appropriate input controls and validation.
ExceptionsexceptionsNoDisplay and manage data exceptions and errors associated with data objects.
Taxon TabstaxonTabsNoTabbed interface for managing taxonomy-level data, with tabs derived from taxon structure.

Using Components in V2 Nodes

Reference any registered component by its type in the component field of a UINode:
{
  "version": "2",
  "nodes": [
    {
      "component": "card:cardPanel",
      "props": {
        "title": "Invoice Details",
        "groupTaxon": "invoice",
        "showHeader": true
      },
      "children": [
        {
          "component": "card:label",
          "props": { "label": "Invoice Number" }
        },
        {
          "component": "card:dataAttributeEditor",
          "props": { "taxon": "invoice/invoiceNumber" }
        },
        {
          "component": "card:dataAttributeEditor",
          "props": { "taxon": "invoice/totalAmount" }
        }
      ]
    },
    {
      "component": "card:taxonGrid",
      "props": {
        "groupTaxon": "lineItem"
      }
    }
  ]
}

Checking Children Support

Only components with supportsChildren: true can contain child nodes. If you add children to a component that does not support them, the children will be ignored. Containers that support children: cardPanel, cardGroup, tabs.

Layout Components

In addition to card components, V2 forms support layout through CSS classes and the class and style fields on UINode. The grid layout system from V1 (layout.x, layout.y, layout.w, layout.h) continues to work for card components that expect it. For V2-specific layout, use CSS utility classes:
{
  "component": "card:cardPanel",
  "class": "mt-4 mb-2",
  "style": { "maxWidth": "800px" },
  "children": [ ... ]
}

Registering Custom Components

The component registry supports runtime registration of custom components. This allows extensions and plugins to add new component types that can be referenced in V2 form schemas.

Registration API

import { getGlobalRegistry } from "@/schema/registry";

const registry = getGlobalRegistry();

registry.register("custom", {
  type: "myWidget",
  name: "My Custom Widget",
  category: "custom",
  implementation: () => MyWidgetComponent,
  options: [],
  supportsChildren: false,
  defaults: { color: "blue" },
  emits: {
    "value-changed": {
      payload: "{ value: any, previous: any }",
      description: "Fired when the widget value changes",
    },
  },
});

Using Custom Components

Once registered, custom components are referenced by their full key:
{
  "component": "custom:myWidget",
  "props": { "color": "red" },
  "events": {
    "value-changed": {
      "type": "script",
      "target": "kodexa.log.debug('Widget value changed')"
    }
  }
}

Custom Component Requirements

Custom components should:
  1. Accept props via Vue defineProps: The renderer passes resolved props (from both props and bindings) via v-bind.
  2. Emit events via Vue defineEmits: The renderer attaches handlers via v-on based on the events configuration.
  3. Use the default slot for children: If supportsChildren is true, child nodes are rendered in the component’s default slot.
<script setup lang="ts">
defineProps<{
  color?: string;
  label?: string;
}>();

const emit = defineEmits<{
  (e: "value-changed", payload: { value: any }): void;
}>();
</script>

<template>
  <div :style="{ color }">
    {{ label }}
    <slot /> <!-- children render here -->
  </div>
</template>

Common Patterns

Form with Mixed Component Types

{
  "version": "2",
  "nodes": [
    {
      "component": "card:label",
      "props": { "label": "Document Overview" }
    },
    {
      "component": "card:cardPanel",
      "props": { "title": "Header", "groupTaxon": "invoice" },
      "children": [
        { "component": "card:dataAttributeEditor", "props": { "taxon": "invoice/vendorName" } },
        { "component": "card:dataAttributeEditor", "props": { "taxon": "invoice/invoiceDate" } }
      ]
    },
    {
      "component": "card:transposedGridRollup",
      "props": { "groupTaxon": "lineItem" }
    },
    {
      "component": "card:exceptions"
    }
  ]
}

Querying the Registry Programmatically

const registry = getGlobalRegistry();

// Get all card components
const cards = registry.getByCategory("card");

// Check if a component exists
const def = registry.resolve("card:taxonGrid");
if (def) {
  console.log(def.name, "supports children:", def.supportsChildren);
}

Next Steps