Skip to content

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.

ts
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 ​

ts
type SchemaNodeType =
  | 'VerticalLayout'
  | 'HorizontalLayout'
  | 'Group'
  | 'Categorization'
  | 'Category'
  | 'Control'
  | 'Label'

SchemaNodeMeta ​

ts
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.

ts
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 ​

ts
type JsonSchemaType =
  | 'string'
  | 'number'
  | 'integer'
  | 'boolean'
  | 'array'
  | 'object'
  | 'null'

BuilderRule / BuilderCondition ​

ts
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 ​

ts
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 ​

ts
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:

ts
type PreviewAdapterType = 'jsonforms-vue-vanilla' | 'jsonforms-vue-vuetify' | 'none'

Note: This is exported from @banclo/jsonforms-core as PreviewAdapter (a type alias). Do not confuse it with the PreviewAdapter interface from @banclo/jsonforms-preview, which defines the full adapter object shape (with id, 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.

ts
function createSchemaNode(
  type: SchemaNodeType,
  overrides?: Partial<SchemaNode>,
): SchemaNode

Default values by type:

TypeDefaults
VerticalLayoutlabel: false, empty children
HorizontalLayoutlabel: false, empty children
Grouplabel: 'Group', empty children
Categorizationlabel: 'Categorization', empty children
Categorylabel: 'Category', empty children
ControlpropertyName: 'newField', schemaProperties: { type: 'string' }, label: true
Labellabel: 'Label text'

Example:

ts
const control = createSchemaNode('Control', {
  propertyName: 'email',
  schemaProperties: { type: 'string', format: 'email' },
  label: 'Email',
})

createInitialDragState ​

ts
function createInitialDragState(): DragState

Returns a DragState with all fields set to null / false.

createInitialSelectionState ​

ts
function createInitialSelectionState(): SelectionState

createDefaultBuilderConfig ​

ts
function createDefaultBuilderConfig(): BuilderConfig

Returns 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 ​

PropertyTypeDescription
rootNodeRef<SchemaNode>The root of the form tree
selectedNodeIdRef<string | null>Currently selected node ID
hoveredNodeIdRef<string | null>Currently hovered node ID
dragStateRef<DragState | null>Current drag operation state
nodeMapRef<Map<string, SchemaNode>>Flat lookup map of all nodes
formMetaRef<FormMeta>Form-level metadata

Computed Getters ​

GetterTypeDescription
selectedNodeSchemaNode | nullThe currently selected node object
hoveredNodeSchemaNode | nullThe currently hovered node object
jsonSchemaRecord<string, unknown>Generated JSON Schema
uiSchemaRecord<string, unknown>Generated UI Schema
allPropertyNamesstring[]All property names from Control nodes
canUndobooleanWhether undo is available
canRedobooleanWhether redo is available

Actions ​

selectNode ​
ts
function selectNode(id: string | null): void

Set the selected node. Pass null to deselect.

hoverNode ​
ts
function hoverNode(id: string | null): void

Set the hovered node for visual feedback.

addNode ​
ts
function addNode(manifestId: string, parentId: string, index?: number): SchemaNode

Create 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 ​
ts
function removeNode(nodeId: string): void

Remove a node and its subtree. Cannot remove the root. Clears selection if the removed node was selected. Pushes history.

moveNode ​
ts
function moveNode(nodeId: string, newParentId: string, newIndex: number): void

Move a node to a new parent at a new index. Handles index adjustment when moving within the same parent. Pushes history.

updateNode ​
ts
function updateNode(nodeId: string, updates: Partial<SchemaNode>): void

Apply partial updates to a node. The id field cannot be changed. Pushes history.

updateNodeProperty ​
ts
function updateNodeProperty(nodeId: string, path: string, value: unknown): void

Update a nested property using dot-notation path. Creates intermediate objects if needed.

ts
store.updateNodeProperty(nodeId, 'schemaProperties.type', 'number')
store.updateNodeProperty(nodeId, 'options.format', 'date')
duplicateNode ​
ts
function duplicateNode(nodeId: string): void

Deep-clone a node and its subtree, assign fresh IDs and unique property names, and insert the clone after the original. Pushes history.

setDragState ​
ts
function setDragState(state: DragState | null): void
setFormMeta ​
ts
function setFormMeta(meta: Partial<FormMeta>): void
importSchema ​
ts
function importSchema(jsonSchema: object, uiSchema: object): void

Parse a JSON Schema + UI Schema pair into a SchemaNode tree and replace the current tree. Pushes history.

exportSchema ​
ts
function exportSchema(): {
  jsonSchema: Record<string, unknown>
  uiSchema: Record<string, unknown>
}

Return the current generated JSON Schema and UI Schema.

reset ​
ts
function reset(): void

Clear the entire builder to a fresh empty state. Pushes history.

undo / redo ​
ts
function undo(): void
function redo(): void

Undo or redo the last tree operation.

useHistoryStore ​

Manages undo/redo via JSON snapshots.

MemberTypeDescription
undoStackRef<string[]>Stack of JSON-serialized tree snapshots
redoStackRef<string[]>Stack of redo snapshots
canUndoComputedRef<boolean>Whether the undo stack is non-empty
canRedoComputedRef<boolean>Whether the redo stack is non-empty
pushSnapshot(rootNode)ActionPush current state onto undo stack (max 50)
undo(callback, current)ActionPop undo stack, push current to redo, call callback
redo(callback, current)ActionPop redo stack, push current to undo, call callback
clear()ActionClear both stacks

useClipboardStore ​

Handles copy/cut/paste of node subtrees.

MemberTypeDescription
clipboardDataRef<string | null>JSON-serialized copied node
sourceNodeIdRef<string | null>ID of the originally copied node
hasCopiedDataComputedRef<boolean>Whether the clipboard has data
copy(node)ActionSerialize a node to the clipboard
paste(parentId, index, callback)ActionClone, regenerate IDs, call callback to insert
cut(node, removeCallback)ActionCopy then remove the original node
clear()ActionClear the clipboard

Operations ​

All tree operations are pure functions that return a new root node. The original tree is never mutated.

addElement ​

ts
function addElement(
  rootNode: SchemaNode,
  newNode: SchemaNode,
  parentId: string,
  index?: number,
): SchemaNode

Insert newNode as a child of the node with parentId at index. If index is omitted, appends at the end.

removeElement ​

ts
function removeElement(rootNode: SchemaNode, nodeId: string): SchemaNode

Remove the node with nodeId and all its descendants. Throws if attempting to remove the root.

moveElement ​

ts
function moveElement(
  rootNode: SchemaNode,
  nodeId: string,
  newParentId: string,
  newIndex: number,
): SchemaNode

Move a node from its current position to newParentId at newIndex. Adjusts the index automatically when moving within the same parent.

updateElement ​

ts
function updateElement(
  rootNode: SchemaNode,
  nodeId: string,
  updates: Partial<SchemaNode>,
): SchemaNode

Apply partial updates to a node. The id field is ignored in updates.

updateNestedProperty ​

ts
function updateNestedProperty(
  rootNode: SchemaNode,
  nodeId: string,
  path: string,
  value: unknown,
): SchemaNode

Update a nested property at a dot-separated path. Creates intermediate objects as needed.

syncPropertyNames ​

ts
function syncPropertyNames(rootNode: SchemaNode): SchemaNode

Ensure all Control nodes have unique property names. Assigns names to controls without one and appends numeric suffixes to resolve duplicates.

resolvePropertyConflicts ​

ts
function resolvePropertyConflicts(rootNode: SchemaNode): SchemaNode

Rename duplicate property names by appending numeric suffixes.

Schema Generation ​

generateJsonSchema ​

ts
function generateJsonSchema(rootNode: SchemaNode): object

Walk 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 ​

ts
function generateUiSchema(rootNode: SchemaNode): object

Recursively 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 ​

ts
function importSchemas(jsonSchema: object, uiSchema: object): SchemaNode

Parse 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: importSchemas is not re-exported from @banclo/jsonforms-core's main entry point. It is available via the store action store.importSchema(), or by importing directly from the subpath:

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

Tree Utilities ​

findNode ​

ts
function findNode(root: SchemaNode, id: string): SchemaNode | null

Find a node by ID using depth-first search.

findParent ​

ts
function findParent(root: SchemaNode, childId: string): SchemaNode | null

Find the parent of a node by the child's ID.

walkTree ​

ts
function walkTree(
  root: SchemaNode,
  visitor: (node: SchemaNode) => void | false,
): void

Depth-first traversal. If the visitor returns false, the subtree's children are skipped.

flattenTree ​

ts
function flattenTree(root: SchemaNode): SchemaNode[]

Return all nodes in depth-first order as a flat array.

buildNodeMap ​

ts
function buildNodeMap(root: SchemaNode): Map<string, SchemaNode>

Build a Map<string, SchemaNode> for O(1) lookups.

cloneSubtree ​

ts
function cloneSubtree(node: SchemaNode): SchemaNode

Deep-clone a subtree with fresh IDs. Parent-child ID relationships are maintained within the clone.

getPath ​

ts
function getPath(root: SchemaNode, nodeId: string): string[]

Return the path of ancestor IDs from root to the given node (inclusive).

getDepth ​

ts
function getDepth(root: SchemaNode, nodeId: string): number

Return the nesting depth (root = 0). Returns -1 if not found.

Scope Utilities ​

propertyNameToScope ​

ts
function propertyNameToScope(name: string): string
// propertyNameToScope("firstName") => "#/properties/firstName"

scopeToPropertyName ​

ts
function scopeToPropertyName(scope: string): string
// scopeToPropertyName("#/properties/firstName") => "firstName"
// scopeToPropertyName("#/properties/address/properties/street") => "street"

buildNestedScope ​

ts
function buildNestedScope(path: string[]): string
// buildNestedScope(["address", "street"]) => "#/properties/address/properties/street"

isValidScope ​

ts
function isValidScope(scope: string): boolean

Validate that a scope string follows the #/properties/name format.

ID Utilities ​

generateId ​

ts
function generateId(): string

Generate a unique ID. Uses crypto.randomUUID() when available, falls back to a timestamp+random string.

generatePropertyName ​

ts
function generatePropertyName(baseName: string, existingNames: Iterable<string>): string

Generate a unique property name by appending numeric suffixes if baseName already exists in existingNames.

Validation ​

validateSchemaNode ​

ts
function validateSchemaNode(node: SchemaNode): ValidationResult

Validate the tree for structural correctness. See Recipes > Custom Validation for details.

Note: validateSchemaNode is not re-exported from @banclo/jsonforms-core's main entry point. Import it directly from the validator subpath:

ts
import { 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 main index.ts.

validateGeneratedSchemas ​

ts
function validateGeneratedSchemas(jsonSchema: object, uiSchema: object): ValidationResult

Validate 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:

ts
import { validateGeneratedSchemas } from '@banclo/jsonforms-core/codegen/validator'

ValidationResult ​

ts
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: useDragDrop is 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.

ts
const { onDragAdd, onDragUpdate, onMoveNode } = useDragDrop()
FunctionSignatureDescription
onDragAdd(manifestId: string, parentId: string, index: number) => voidHandle a new item dropped from the palette
onDragUpdate(parentId: string, oldIndex: number, newIndex: number) => voidHandle reordering within the same container
onMoveNode(nodeId: string, newParentId: string, newIndex: number) => voidHandle moving a node to a different container

Correction: Some earlier documentation only listed { onDragAdd, onDragUpdate } in the destructured return. The composable also returns onMoveNode for cross-container moves. Additionally, onDragUpdate takes three arguments (parentId, oldIndex, newIndex), not two.

JsonSchemaBuilder ​

A fluent API for constructing JsonSchemaFragment objects:

ts
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().

MethodApplies ToDescription
.title(s)AllSet the title annotation
.description(s)AllSet the description annotation
.format(f)stringSet the format (email, date, uri, etc.)
.minLength(n)stringMinimum string length
.maxLength(n)stringMaximum string length
.pattern(p)stringRegex pattern
.minimum(n)number/integerMinimum value (inclusive)
.maximum(n)number/integerMaximum value (inclusive)
.exclusiveMinimum(n)number/integerMinimum value (exclusive)
.exclusiveMaximum(n)number/integerMaximum value (exclusive)
.multipleOf(n)number/integerMust be a multiple of n
.enumValues(arr)AllSet allowed values
.constValue(v)AllSet a fixed value
.defaultValue(v)AllSet the default value
.items(schema)arraySet the array items schema
.minItems(n)arrayMinimum array length
.maxItems(n)arrayMaximum array length
.uniqueItems(b)arrayWhether items must be unique
.properties(p)objectSet object properties
.required(fields)objectSet required field names
.additionalProperties(v)objectAllow/disallow extra properties
.oneOf(schemas)AllComposition: exactly one must match
.anyOf(schemas)AllComposition: at least one must match
.allOf(schemas)AllComposition: all must match
.build()AllReturn the final JsonSchemaFragment