Skip to content

Component Manifest Reference ​

Component manifests define the draggable components available in the builder's palette. Each manifest describes a component's type, category, display name, and default node properties.

ComponentManifest Interface ​

ts
interface ComponentManifest {
  /** Unique manifest ID used to create nodes from palette drags */
  id: string

  /**
   * Display name shown in the palette.
   * Should be short and descriptive (e.g., "Text Field", "Horizontal Layout").
   */
  name: string

  /** Optional longer description for tooltips or documentation */
  description?: string

  /** Icon identifier for display in the palette */
  icon: string

  /**
   * Category for grouping in the palette.
   * Must be one of: "layout", "control", "display".
   */
  category: ManifestCategory

  /**
   * The SchemaNode type this manifest creates.
   * Must be one of: VerticalLayout, HorizontalLayout, Group,
   * Categorization, Category, Control, Label.
   */
  nodeType: SchemaNodeType

  /**
   * Default SchemaNode property overrides applied when
   * a new node is created from this manifest.
   */
  defaults: SchemaNodeDefaults

  /**
   * JSON Schema and UI Schema used to render the property editor
   * panel for nodes created from this manifest.
   */
  propertySchema: {
    jsonSchema: Record<string, unknown>
    uiSchema: Record<string, unknown>
  }

  /**
   * Optional matching criteria used to associate existing JSON Schema
   * properties with this manifest during import.
   */
  schemaMatch?: {
    types?: JsonSchemaType[]
    formats?: string[]
  }

  /**
   * Optional placement constraints for the builder tree.
   */
  placement?: {
    allowedParents?: SchemaNodeType[]
    isContainer?: boolean
    allowedChildren?: SchemaNodeType[]
  }

  /**
   * Optional custom preview renderer for the builder canvas.
   */
  previewRenderer?: { tester: Record<string, unknown>; component: unknown }
}

SchemaNodeDefaults ​

The defaults object provides initial values for nodes created from this manifest. It is a restricted type with the following fields:

ts
interface SchemaNodeDefaults {
  propertyName?: string
  schemaProperties?: JsonSchemaFragment
  options?: Record<string, unknown>
  label?: string | boolean
}
FieldTypeTypical Usage
propertyNamestringBase name for auto-generated property names
schemaPropertiesJsonSchemaFragmentSet the JSON Schema type, constraints, format
optionsRecord<string, unknown>UI options passed through to the UI Schema
labelstring | booleanDefault label text or auto-label behavior

Example Defaults ​

ts
// A simple string input
defaults: {
  propertyName: 'text',
  schemaProperties: { type: 'string' },
}

// An email input with validation
defaults: {
  propertyName: 'email',
  schemaProperties: {
    type: 'string',
    format: 'email',
    maxLength: 254,
  },
  label: 'Email Address',
}

// A numeric slider
defaults: {
  propertyName: 'number',
  schemaProperties: {
    type: 'number',
    minimum: 0,
    maximum: 100,
  },
  options: {
    slider: true,
  },
}

ManifestCategory ​

Categories control how components are grouped in the palette. The palette renders one collapsible group per unique category.

ts
type ManifestCategory = 'layout' | 'control' | 'display'

Built-in categories:

CategoryComponents
layoutVerticalLayout, HorizontalLayout, Group, Categorization, Category
controlString, Number, Integer, Boolean, Date, DateTime, Enum, MultiEnum, Array, Object, OneOf controls
displayLabel

Placement Constraints ​

Some node types have placement constraints enforced by the builder's validation:

ConstraintRule
CategoryCan only be a direct child of Categorization
CategorizationCan only contain Category children
ControlsCannot have children (leaf nodes)
LabelsCannot have children (leaf nodes)

The builder does not prevent invalid placements during drag-and-drop, but validateSchemaNode() reports placement violations as errors.

Property Schema Structure ​

When a Control manifest sets schemaProperties, those properties flow into the generated JSON Schema. Here is how each JSON Schema type maps:

String Controls ​

ts
defaults: {
  propertyName: 'text',
  schemaProperties: {
    type: 'string',
    // Optional constraints
    minLength: 1,
    maxLength: 255,
    pattern: '^[A-Z].*',
    format: 'email',       // or: date, uri, date-time, etc.
    // Optional enum
    enum: ['option1', 'option2'],
  },
}

Number / Integer Controls ​

ts
defaults: {
  propertyName: 'number',
  schemaProperties: {
    type: 'number',  // or 'integer'
    minimum: 0,
    maximum: 100,
    exclusiveMinimum: 0,
    exclusiveMaximum: 101,
    multipleOf: 0.5,
  },
}

Boolean Controls ​

ts
defaults: {
  propertyName: 'flag',
  schemaProperties: {
    type: 'boolean',
    default: false,
  },
}

Array Controls ​

ts
defaults: {
  propertyName: 'items',
  schemaProperties: {
    type: 'array',
    items: { type: 'string' },
    minItems: 1,
    maxItems: 10,
    uniqueItems: true,
  },
}

Object Controls ​

ts
defaults: {
  propertyName: 'obj',
  schemaProperties: {
    type: 'object',
    properties: {
      street: { type: 'string' },
      city: { type: 'string' },
    },
    required: ['street'],
    additionalProperties: false,
  },
}

Built-in Manifest Examples ​

vertical-layout ​

ts
{
  id: 'vertical-layout',
  name: 'Vertical Layout',
  category: 'layout',
  nodeType: 'VerticalLayout',
  defaults: {},
}

Generated UI Schema:

json
{ "type": "VerticalLayout", "elements": [] }

horizontal-layout ​

ts
{
  id: 'horizontal-layout',
  name: 'Horizontal Layout',
  category: 'layout',
  nodeType: 'HorizontalLayout',
  defaults: {},
}

Generated UI Schema:

json
{ "type": "HorizontalLayout", "elements": [] }

group ​

ts
{
  id: 'group',
  name: 'Group',
  category: 'layout',
  nodeType: 'Group',
  defaults: { label: 'Group' },
}

Generated UI Schema:

json
{ "type": "Group", "label": "Group", "elements": [] }

categorization ​

ts
{
  id: 'categorization',
  name: 'Categorization',
  category: 'layout',
  nodeType: 'Categorization',
  defaults: {},
}

Generated UI Schema:

json
{ "type": "Categorization", "elements": [] }

category ​

ts
{
  id: 'category',
  name: 'Category',
  category: 'layout',
  nodeType: 'Category',
  defaults: { label: 'Category' },
}

Generated UI Schema:

json
{ "type": "Category", "label": "Category", "elements": [] }

string-control ​

ts
{
  id: 'string-control',
  name: 'Text Field',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'text',
    schemaProperties: { type: 'string' },
  },
}

Generated JSON Schema property:

json
{ "text": { "type": "string" } }

Generated UI Schema element:

json
{ "type": "Control", "scope": "#/properties/text" }

number-control ​

ts
{
  id: 'number-control',
  name: 'Number Field',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'number',
    schemaProperties: { type: 'number' },
  },
}

integer-control ​

ts
{
  id: 'integer-control',
  name: 'Integer Field',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'integer',
    schemaProperties: { type: 'integer' },
  },
}

boolean-control ​

ts
{
  id: 'boolean-control',
  name: 'Checkbox',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'flag',
    schemaProperties: { type: 'boolean' },
  },
}

enum-control ​

ts
{
  id: 'enum-control',
  name: 'Dropdown',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'choice',
    schemaProperties: { type: 'string', enum: ['Option A', 'Option B', 'Option C'] },
  },
}

date-control ​

ts
{
  id: 'date-control',
  name: 'Date Picker',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'date',
    schemaProperties: { type: 'string', format: 'date' },
  },
}

datetime-control ​

ts
{
  id: 'datetime-control',
  name: 'Date-Time Picker',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'dateTime',
    schemaProperties: { type: 'string', format: 'date-time' },
  },
}

multi-enum-control ​

ts
{
  id: 'multi-enum-control',
  name: 'Multi Select',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'selections',
    schemaProperties: { type: 'array', uniqueItems: true, items: { type: 'string', enum: [] } },
  },
}

array-control ​

ts
{
  id: 'array-control',
  name: 'Array',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'items',
    schemaProperties: { type: 'array' },
  },
}

object-control ​

ts
{
  id: 'object-control',
  name: 'Object',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'obj',
    schemaProperties: { type: 'object' },
  },
}

oneOf-control ​

ts
{
  id: 'oneOf-control',
  name: 'OneOf',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'variant',
    schemaProperties: { oneOf: [] },
  },
}

label ​

ts
{
  id: 'label',
  name: 'Label',
  category: 'display',
  nodeType: 'Label',
  defaults: { label: 'Label text' },
}

Generated UI Schema:

json
{ "type": "Label", "text": "Label text" }

Auto-Generated Property Names ​

When a Control manifest is used to create a new node, the builder automatically generates a unique property name based on the manifest's defaults.propertyName:

  • string-control generates text, text1, text2, etc.
  • number-control generates number, number1, number2, etc.
  • integer-control generates integer, integer1, integer2, etc.
  • boolean-control generates flag, flag1, flag2, etc.
  • enum-control generates choice, choice1, choice2, etc.
  • date-control generates date, date1, date2, etc.
  • datetime-control generates dateTime, dateTime1, dateTime2, etc.
  • multi-enum-control generates selections, selections1, selections2, etc.
  • array-control generates items, items1, items2, etc.
  • object-control generates obj, obj1, obj2, etc.
  • oneOf-control generates variant, variant1, variant2, etc.

The generatePropertyName() utility checks against all existing property names in the tree to avoid collisions.

Registering Manifests ​

Use the component registry to add custom manifests:

ts
import { useComponentRegistry } from '@banclo/jsonforms-core'

const registry = useComponentRegistry()

// Register a single manifest
registry.register({
  id: 'phone-input',
  name: 'Phone Number',
  category: 'control',
  nodeType: 'Control',
  defaults: {
    propertyName: 'phone',
    schemaProperties: {
      type: 'string',
      pattern: '^\\+?[1-9]\\d{1,14}$',
    },
    options: { format: 'tel' },
    label: 'Phone',
  },
})

// Retrieve all manifests
const all = registry.getAll()

// Retrieve by ID
const manifest = registry.getById('phone-input')

See Custom Components for complete examples.