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