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
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:
interface SchemaNodeDefaults {
propertyName?: string
schemaProperties?: JsonSchemaFragment
options?: Record<string, unknown>
label?: string | boolean
}| Field | Type | Typical Usage |
|---|---|---|
propertyName | string | Base name for auto-generated property names |
schemaProperties | JsonSchemaFragment | Set the JSON Schema type, constraints, format |
options | Record<string, unknown> | UI options passed through to the UI Schema |
label | string | boolean | Default label text or auto-label behavior |
Example Defaults
// 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.
type ManifestCategory = 'layout' | 'control' | 'display'Built-in categories:
| Category | Components |
|---|---|
layout | VerticalLayout, HorizontalLayout, Group, Categorization, Category |
control | String, Number, Integer, Boolean, Date, DateTime, Enum, MultiEnum, Array, Object, OneOf controls |
display | Label |
Placement Constraints
Some node types have placement constraints enforced by the builder's validation:
| Constraint | Rule |
|---|---|
Category | Can only be a direct child of Categorization |
Categorization | Can only contain Category children |
| Controls | Cannot have children (leaf nodes) |
| Labels | Cannot 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
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
defaults: {
propertyName: 'number',
schemaProperties: {
type: 'number', // or 'integer'
minimum: 0,
maximum: 100,
exclusiveMinimum: 0,
exclusiveMaximum: 101,
multipleOf: 0.5,
},
}Boolean Controls
defaults: {
propertyName: 'flag',
schemaProperties: {
type: 'boolean',
default: false,
},
}Array Controls
defaults: {
propertyName: 'items',
schemaProperties: {
type: 'array',
items: { type: 'string' },
minItems: 1,
maxItems: 10,
uniqueItems: true,
},
}Object Controls
defaults: {
propertyName: 'obj',
schemaProperties: {
type: 'object',
properties: {
street: { type: 'string' },
city: { type: 'string' },
},
required: ['street'],
additionalProperties: false,
},
}Built-in Manifest Examples
vertical-layout
{
id: 'vertical-layout',
name: 'Vertical Layout',
category: 'layout',
nodeType: 'VerticalLayout',
defaults: {},
}Generated UI Schema:
{ "type": "VerticalLayout", "elements": [] }horizontal-layout
{
id: 'horizontal-layout',
name: 'Horizontal Layout',
category: 'layout',
nodeType: 'HorizontalLayout',
defaults: {},
}Generated UI Schema:
{ "type": "HorizontalLayout", "elements": [] }group
{
id: 'group',
name: 'Group',
category: 'layout',
nodeType: 'Group',
defaults: { label: 'Group' },
}Generated UI Schema:
{ "type": "Group", "label": "Group", "elements": [] }categorization
{
id: 'categorization',
name: 'Categorization',
category: 'layout',
nodeType: 'Categorization',
defaults: {},
}Generated UI Schema:
{ "type": "Categorization", "elements": [] }category
{
id: 'category',
name: 'Category',
category: 'layout',
nodeType: 'Category',
defaults: { label: 'Category' },
}Generated UI Schema:
{ "type": "Category", "label": "Category", "elements": [] }string-control
{
id: 'string-control',
name: 'Text Field',
category: 'control',
nodeType: 'Control',
defaults: {
propertyName: 'text',
schemaProperties: { type: 'string' },
},
}Generated JSON Schema property:
{ "text": { "type": "string" } }Generated UI Schema element:
{ "type": "Control", "scope": "#/properties/text" }number-control
{
id: 'number-control',
name: 'Number Field',
category: 'control',
nodeType: 'Control',
defaults: {
propertyName: 'number',
schemaProperties: { type: 'number' },
},
}integer-control
{
id: 'integer-control',
name: 'Integer Field',
category: 'control',
nodeType: 'Control',
defaults: {
propertyName: 'integer',
schemaProperties: { type: 'integer' },
},
}boolean-control
{
id: 'boolean-control',
name: 'Checkbox',
category: 'control',
nodeType: 'Control',
defaults: {
propertyName: 'flag',
schemaProperties: { type: 'boolean' },
},
}enum-control
{
id: 'enum-control',
name: 'Dropdown',
category: 'control',
nodeType: 'Control',
defaults: {
propertyName: 'choice',
schemaProperties: { type: 'string', enum: ['Option A', 'Option B', 'Option C'] },
},
}date-control
{
id: 'date-control',
name: 'Date Picker',
category: 'control',
nodeType: 'Control',
defaults: {
propertyName: 'date',
schemaProperties: { type: 'string', format: 'date' },
},
}datetime-control
{
id: 'datetime-control',
name: 'Date-Time Picker',
category: 'control',
nodeType: 'Control',
defaults: {
propertyName: 'dateTime',
schemaProperties: { type: 'string', format: 'date-time' },
},
}multi-enum-control
{
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
{
id: 'array-control',
name: 'Array',
category: 'control',
nodeType: 'Control',
defaults: {
propertyName: 'items',
schemaProperties: { type: 'array' },
},
}object-control
{
id: 'object-control',
name: 'Object',
category: 'control',
nodeType: 'Control',
defaults: {
propertyName: 'obj',
schemaProperties: { type: 'object' },
},
}oneOf-control
{
id: 'oneOf-control',
name: 'OneOf',
category: 'control',
nodeType: 'Control',
defaults: {
propertyName: 'variant',
schemaProperties: { oneOf: [] },
},
}label
{
id: 'label',
name: 'Label',
category: 'display',
nodeType: 'Label',
defaults: { label: 'Label text' },
}Generated UI Schema:
{ "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-controlgeneratestext,text1,text2, etc.number-controlgeneratesnumber,number1,number2, etc.integer-controlgeneratesinteger,integer1,integer2, etc.boolean-controlgeneratesflag,flag1,flag2, etc.enum-controlgenerateschoice,choice1,choice2, etc.date-controlgeneratesdate,date1,date2, etc.datetime-controlgeneratesdateTime,dateTime1,dateTime2, etc.multi-enum-controlgeneratesselections,selections1,selections2, etc.array-controlgeneratesitems,items1,items2, etc.object-controlgeneratesobj,obj1,obj2, etc.oneOf-controlgeneratesvariant,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:
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.