Core API Reference
@banclo/jsonforms-core provides state management, tree operations, schema generation, and type definitions.
Types
SchemaNode
The primary data structure representing a single element in the form builder tree.
interface SchemaNode {
/** Unique identifier (UUID) */
id: string
/** The element type */
type: SchemaNodeType
/** JSON Schema property name (Control nodes only) */
propertyName?: string
/** Full JSON Forms scope string, e.g. "#/properties/address/properties/street" */
scope?: string
/** JSON Schema fragment describing the data type and constraints */
schemaProperties?: JsonSchemaFragment
/** Arbitrary options passed through to the UI Schema */
options?: Record<string, unknown>
/** Display label -- string for text, boolean to show/hide auto-label */
label?: string | boolean
/** i18n key for label translation */
i18n?: string
/** Ordered child nodes */
children: SchemaNode[]
/** ID of the parent node, null for root */
parentId: string | null
/** Conditional visibility/enablement rule */
rule?: BuilderRule
/** Builder-only metadata (not included in output schemas) */
_meta: SchemaNodeMeta
}SchemaNodeType
type SchemaNodeType =
| 'VerticalLayout'
| 'HorizontalLayout'
| 'Group'
| 'Categorization'
| 'Category'
| 'Control'
| 'Label'SchemaNodeMeta
interface SchemaNodeMeta {
/** Whether the node's children are expanded in the canvas */
expanded: boolean
/** Whether the node is locked from editing */
locked: boolean
/** Whether the node is hidden in the canvas (but still in the tree) */
hidden: boolean
}JsonSchemaFragment
A partial JSON Schema object attached to a Control node.
interface JsonSchemaFragment {
type?: JsonSchemaType
format?: string
// Enum / const / default
enum?: unknown[]
const?: unknown
default?: unknown
// Annotations
title?: string
description?: string
// String constraints
minLength?: number
maxLength?: number
pattern?: string
// Numeric constraints
minimum?: number
maximum?: number
exclusiveMinimum?: number
exclusiveMaximum?: number
multipleOf?: number
// Array constraints
minItems?: number
maxItems?: number
uniqueItems?: boolean
items?: JsonSchemaFragment
// Object constraints
properties?: Record<string, JsonSchemaFragment>
required?: string[]
additionalProperties?: boolean | JsonSchemaFragment
minProperties?: number
maxProperties?: number
// Composition
oneOf?: JsonSchemaFragment[]
anyOf?: JsonSchemaFragment[]
allOf?: JsonSchemaFragment[]
// Extensible
[key: string]: unknown
}JsonSchemaType
type JsonSchemaType =
| 'string'
| 'number'
| 'integer'
| 'boolean'
| 'array'
| 'object'
| 'null'BuilderRule / BuilderCondition
interface BuilderRule {
effect: RuleEffect
condition: BuilderCondition
}
type RuleEffect = 'SHOW' | 'HIDE' | 'ENABLE' | 'DISABLE'
type BuilderCondition = LeafCondition | CompositeCondition | SchemaCondition
interface LeafCondition {
type: 'LEAF'
scope: string
expectedValue: unknown
}
interface CompositeCondition {
type: 'AND' | 'OR'
conditions: BuilderCondition[]
}
interface SchemaCondition {
type: 'SCHEMA'
scope: string
schema: JsonSchemaFragment
}DragState
interface DragState {
/** ID of the node being dragged, or null if from palette */
sourceNodeId: string | null
/** Manifest ID when dragging from the palette */
sourceManifestId: string | null
/** Current drop target node ID */
targetNodeId: string | null
/** Drop position relative to target */
targetPosition: DropPosition | null
/** Whether a drag is in progress */
isDragging: boolean
}
type DropPosition = 'before' | 'after' | 'inside'BuilderConfig
interface BuilderConfig {
previewAdapter: PreviewAdapterType
theme: string
showSchemaPanel: boolean
showPreviewPanel: boolean
locale: string
dragDropEnabled: boolean
allowedRootTypes: SchemaNodeType[]
}PreviewAdapterType
The PreviewAdapterType in @banclo/jsonforms-core is a string union that selects which renderer set to use:
type PreviewAdapterType = 'jsonforms-vue-vanilla' | 'jsonforms-vue-vuetify' | 'none'Note: This is exported from
@banclo/jsonforms-coreasPreviewAdapter(a type alias). Do not confuse it with thePreviewAdapterinterface from@banclo/jsonforms-preview, which defines the full adapter object shape (withid,name, renderers, setup logic, etc.). The core type is just the identifier string; the preview interface is the runtime adapter implementation.
Factory Functions
createSchemaNode
Create a new SchemaNode with sensible defaults for the given type.
function createSchemaNode(
type: SchemaNodeType,
overrides?: Partial<SchemaNode>,
): SchemaNodeDefault values by type:
| Type | Defaults |
|---|---|
VerticalLayout | label: false, empty children |
HorizontalLayout | label: false, empty children |
Group | label: 'Group', empty children |
Categorization | label: 'Categorization', empty children |
Category | label: 'Category', empty children |
Control | propertyName: 'newField', schemaProperties: { type: 'string' }, label: true |
Label | label: 'Label text' |
Example:
const control = createSchemaNode('Control', {
propertyName: 'email',
schemaProperties: { type: 'string', format: 'email' },
label: 'Email',
})createInitialDragState
function createInitialDragState(): DragStateReturns a DragState with all fields set to null / false.
createInitialSelectionState
function createInitialSelectionState(): SelectionStatecreateDefaultBuilderConfig
function createDefaultBuilderConfig(): BuilderConfigReturns a config with vanilla preview adapter, default theme, all panels visible, English locale, drag/drop enabled, and standard root types allowed.
Stores
useBuilderStore
The primary Pinia store for the form builder.
State
| Property | Type | Description |
|---|---|---|
rootNode | Ref<SchemaNode> | The root of the form tree |
selectedNodeId | Ref<string | null> | Currently selected node ID |
hoveredNodeId | Ref<string | null> | Currently hovered node ID |
dragState | Ref<DragState | null> | Current drag operation state |
nodeMap | Ref<Map<string, SchemaNode>> | Flat lookup map of all nodes |
formMeta | Ref<FormMeta> | Form-level metadata |
Computed Getters
| Getter | Type | Description |
|---|---|---|
selectedNode | SchemaNode | null | The currently selected node object |
hoveredNode | SchemaNode | null | The currently hovered node object |
jsonSchema | Record<string, unknown> | Generated JSON Schema |
uiSchema | Record<string, unknown> | Generated UI Schema |
allPropertyNames | string[] | All property names from Control nodes |
canUndo | boolean | Whether undo is available |
canRedo | boolean | Whether redo is available |
Actions
selectNode
function selectNode(id: string | null): voidSet the selected node. Pass null to deselect.
hoverNode
function hoverNode(id: string | null): voidSet the hovered node for visual feedback.
addNode
function addNode(manifestId: string, parentId: string, index?: number): SchemaNodeCreate a new node from a manifest and insert it as a child of parentId at the given index. Internally uses registry.getById() to look up the manifest. Pushes history. Returns the inserted node.
removeNode
function removeNode(nodeId: string): voidRemove a node and its subtree. Cannot remove the root. Clears selection if the removed node was selected. Pushes history.
moveNode
function moveNode(nodeId: string, newParentId: string, newIndex: number): voidMove a node to a new parent at a new index. Handles index adjustment when moving within the same parent. Pushes history.
updateNode
function updateNode(nodeId: string, updates: Partial<SchemaNode>): voidApply partial updates to a node. The id field cannot be changed. Pushes history.
updateNodeProperty
function updateNodeProperty(nodeId: string, path: string, value: unknown): voidUpdate a nested property using dot-notation path. Creates intermediate objects if needed.
store.updateNodeProperty(nodeId, 'schemaProperties.type', 'number')
store.updateNodeProperty(nodeId, 'options.format', 'date')duplicateNode
function duplicateNode(nodeId: string): voidDeep-clone a node and its subtree, assign fresh IDs and unique property names, and insert the clone after the original. Pushes history.
setDragState
function setDragState(state: DragState | null): voidsetFormMeta
function setFormMeta(meta: Partial<FormMeta>): voidimportSchema
function importSchema(jsonSchema: object, uiSchema: object): voidParse a JSON Schema + UI Schema pair into a SchemaNode tree and replace the current tree. Pushes history.
exportSchema
function exportSchema(): {
jsonSchema: Record<string, unknown>
uiSchema: Record<string, unknown>
}Return the current generated JSON Schema and UI Schema.
reset
function reset(): voidClear the entire builder to a fresh empty state. Pushes history.
undo / redo
function undo(): void
function redo(): voidUndo or redo the last tree operation.
useHistoryStore
Manages undo/redo via JSON snapshots.
| Member | Type | Description |
|---|---|---|
undoStack | Ref<string[]> | Stack of JSON-serialized tree snapshots |
redoStack | Ref<string[]> | Stack of redo snapshots |
canUndo | ComputedRef<boolean> | Whether the undo stack is non-empty |
canRedo | ComputedRef<boolean> | Whether the redo stack is non-empty |
pushSnapshot(rootNode) | Action | Push current state onto undo stack (max 50) |
undo(callback, current) | Action | Pop undo stack, push current to redo, call callback |
redo(callback, current) | Action | Pop redo stack, push current to undo, call callback |
clear() | Action | Clear both stacks |
useClipboardStore
Handles copy/cut/paste of node subtrees.
| Member | Type | Description |
|---|---|---|
clipboardData | Ref<string | null> | JSON-serialized copied node |
sourceNodeId | Ref<string | null> | ID of the originally copied node |
hasCopiedData | ComputedRef<boolean> | Whether the clipboard has data |
copy(node) | Action | Serialize a node to the clipboard |
paste(parentId, index, callback) | Action | Clone, regenerate IDs, call callback to insert |
cut(node, removeCallback) | Action | Copy then remove the original node |
clear() | Action | Clear the clipboard |
Operations
All tree operations are pure functions that return a new root node. The original tree is never mutated.
addElement
function addElement(
rootNode: SchemaNode,
newNode: SchemaNode,
parentId: string,
index?: number,
): SchemaNodeInsert newNode as a child of the node with parentId at index. If index is omitted, appends at the end.
removeElement
function removeElement(rootNode: SchemaNode, nodeId: string): SchemaNodeRemove the node with nodeId and all its descendants. Throws if attempting to remove the root.
moveElement
function moveElement(
rootNode: SchemaNode,
nodeId: string,
newParentId: string,
newIndex: number,
): SchemaNodeMove a node from its current position to newParentId at newIndex. Adjusts the index automatically when moving within the same parent.
updateElement
function updateElement(
rootNode: SchemaNode,
nodeId: string,
updates: Partial<SchemaNode>,
): SchemaNodeApply partial updates to a node. The id field is ignored in updates.
updateNestedProperty
function updateNestedProperty(
rootNode: SchemaNode,
nodeId: string,
path: string,
value: unknown,
): SchemaNodeUpdate a nested property at a dot-separated path. Creates intermediate objects as needed.
syncPropertyNames
function syncPropertyNames(rootNode: SchemaNode): SchemaNodeEnsure all Control nodes have unique property names. Assigns names to controls without one and appends numeric suffixes to resolve duplicates.
resolvePropertyConflicts
function resolvePropertyConflicts(rootNode: SchemaNode): SchemaNodeRename duplicate property names by appending numeric suffixes.
Schema Generation
generateJsonSchema
function generateJsonSchema(rootNode: SchemaNode): objectWalk the tree, collect all Control nodes, and produce a JSON Schema (draft-07 compatible) with type: "object" and a properties map. Handles string, number, integer, boolean, array, and object types. Deduplicates property names and marks required fields.
generateUiSchema
function generateUiSchema(rootNode: SchemaNode): objectRecursively convert the node tree into a JSON Forms UI Schema. Layouts become containers with elements arrays. Controls become { type: "Control", scope: "#/properties/..." }. Labels, groups, categorization, rules, and options are all preserved.
importSchemas
function importSchemas(jsonSchema: object, uiSchema: object): SchemaNodeParse a JSON Schema + UI Schema pair back into a SchemaNode tree. Walks the UI Schema recursively and looks up schema properties from the JSON Schema for each Control.
Note:
importSchemasis not re-exported from@banclo/jsonforms-core's main entry point. It is available via the store actionstore.importSchema(), or by importing directly from the subpath:tsimport { importSchemas } from '@banclo/jsonforms-core/codegen'
Tree Utilities
findNode
function findNode(root: SchemaNode, id: string): SchemaNode | nullFind a node by ID using depth-first search.
findParent
function findParent(root: SchemaNode, childId: string): SchemaNode | nullFind the parent of a node by the child's ID.
walkTree
function walkTree(
root: SchemaNode,
visitor: (node: SchemaNode) => void | false,
): voidDepth-first traversal. If the visitor returns false, the subtree's children are skipped.
flattenTree
function flattenTree(root: SchemaNode): SchemaNode[]Return all nodes in depth-first order as a flat array.
buildNodeMap
function buildNodeMap(root: SchemaNode): Map<string, SchemaNode>Build a Map<string, SchemaNode> for O(1) lookups.
cloneSubtree
function cloneSubtree(node: SchemaNode): SchemaNodeDeep-clone a subtree with fresh IDs. Parent-child ID relationships are maintained within the clone.
getPath
function getPath(root: SchemaNode, nodeId: string): string[]Return the path of ancestor IDs from root to the given node (inclusive).
getDepth
function getDepth(root: SchemaNode, nodeId: string): numberReturn the nesting depth (root = 0). Returns -1 if not found.
Scope Utilities
propertyNameToScope
function propertyNameToScope(name: string): string
// propertyNameToScope("firstName") => "#/properties/firstName"scopeToPropertyName
function scopeToPropertyName(scope: string): string
// scopeToPropertyName("#/properties/firstName") => "firstName"
// scopeToPropertyName("#/properties/address/properties/street") => "street"buildNestedScope
function buildNestedScope(path: string[]): string
// buildNestedScope(["address", "street"]) => "#/properties/address/properties/street"isValidScope
function isValidScope(scope: string): booleanValidate that a scope string follows the #/properties/name format.
ID Utilities
generateId
function generateId(): stringGenerate a unique ID. Uses crypto.randomUUID() when available, falls back to a timestamp+random string.
generatePropertyName
function generatePropertyName(baseName: string, existingNames: Iterable<string>): stringGenerate a unique property name by appending numeric suffixes if baseName already exists in existingNames.
Validation
validateSchemaNode
function validateSchemaNode(node: SchemaNode): ValidationResultValidate the tree for structural correctness. See Recipes > Custom Validation for details.
Note:
validateSchemaNodeis not re-exported from@banclo/jsonforms-core's main entry point. Import it directly from the validator subpath:tsimport { validateSchemaNode } from '@banclo/jsonforms-core/codegen/validator'Alternatively, this may be a gap worth fixing by adding a re-export to
@banclo/jsonforms-core's mainindex.ts.
validateGeneratedSchemas
function validateGeneratedSchemas(jsonSchema: object, uiSchema: object): ValidationResultValidate the generated JSON Schema and UI Schema for consistency.
Note: Like
validateSchemaNode, this function is not re-exported from@banclo/jsonforms-core's main entry point. Import it directly:tsimport { validateGeneratedSchemas } from '@banclo/jsonforms-core/codegen/validator'
ValidationResult
interface ValidationResult {
valid: boolean
errors: ValidationError[]
warnings: ValidationWarning[]
}
interface ValidationError {
path: string
message: string
nodeId?: string
}
interface ValidationWarning {
path: string
message: string
nodeId?: string
}Drag and Drop
useDragDrop
Note:
useDragDropis a composable from@banclo/jsonforms-ui, not@banclo/jsonforms-core. It is documented here for completeness since it wraps core store actions.
Provides drag-and-drop event handlers used by BuilderCanvas and CanvasNode.
const { onDragAdd, onDragUpdate, onMoveNode } = useDragDrop()| Function | Signature | Description |
|---|---|---|
onDragAdd | (manifestId: string, parentId: string, index: number) => void | Handle a new item dropped from the palette |
onDragUpdate | (parentId: string, oldIndex: number, newIndex: number) => void | Handle reordering within the same container |
onMoveNode | (nodeId: string, newParentId: string, newIndex: number) => void | Handle moving a node to a different container |
Correction: Some earlier documentation only listed
{ onDragAdd, onDragUpdate }in the destructured return. The composable also returnsonMoveNodefor cross-container moves. Additionally,onDragUpdatetakes three arguments(parentId, oldIndex, newIndex), not two.
JsonSchemaBuilder
A fluent API for constructing JsonSchemaFragment objects:
import { JsonSchemaBuilder } from '@banclo/jsonforms-core'
const schema = new JsonSchemaBuilder('string')
.title('Email Address')
.description('Your primary email')
.format('email')
.minLength(5)
.maxLength(254)
.build()
// => { type: 'string', title: 'Email Address', description: '...', format: 'email', minLength: 5, maxLength: 254 }Methods
All methods return this for chaining, except build().
| Method | Applies To | Description |
|---|---|---|
.title(s) | All | Set the title annotation |
.description(s) | All | Set the description annotation |
.format(f) | string | Set the format (email, date, uri, etc.) |
.minLength(n) | string | Minimum string length |
.maxLength(n) | string | Maximum string length |
.pattern(p) | string | Regex pattern |
.minimum(n) | number/integer | Minimum value (inclusive) |
.maximum(n) | number/integer | Maximum value (inclusive) |
.exclusiveMinimum(n) | number/integer | Minimum value (exclusive) |
.exclusiveMaximum(n) | number/integer | Maximum value (exclusive) |
.multipleOf(n) | number/integer | Must be a multiple of n |
.enumValues(arr) | All | Set allowed values |
.constValue(v) | All | Set a fixed value |
.defaultValue(v) | All | Set the default value |
.items(schema) | array | Set the array items schema |
.minItems(n) | array | Minimum array length |
.maxItems(n) | array | Maximum array length |
.uniqueItems(b) | array | Whether items must be unique |
.properties(p) | object | Set object properties |
.required(fields) | object | Set required field names |
.additionalProperties(v) | object | Allow/disallow extra properties |
.oneOf(schemas) | All | Composition: exactly one must match |
.anyOf(schemas) | All | Composition: at least one must match |
.allOf(schemas) | All | Composition: all must match |
.build() | All | Return the final JsonSchemaFragment |