Examples
Practical examples demonstrating common form builder patterns. Each example shows the code followed by a live interactive demo.
TIP
Only one live demo is shown at a time to keep the builder responsive. Use the tabs below the code samples to switch between demos.
Simple Contact Form
Build a basic contact form with name, email, and message fields:
ts
import { useBuilderStore } from '@banclo/jsonforms-core'
const store = useBuilderStore()
// Add a horizontal layout for first/last name
const layout = store.addNode('horizontal-layout', store.rootNode.id)
// Add name fields inside the horizontal layout
store.addNode('text-control', layout.id) // => "text" field
store.addNode('text-control', layout.id) // => "text1" field
// Rename the fields
const nodes = [...store.nodeMap.values()]
const textNode = nodes.find(n => n.propertyName === 'text')
const text1Node = nodes.find(n => n.propertyName === 'text1')
if (textNode) {
store.updateNode(textNode.id, {
propertyName: 'firstName',
label: 'First Name',
schemaProperties: { type: 'string', minLength: 1 },
})
}
if (text1Node) {
store.updateNode(text1Node.id, {
propertyName: 'lastName',
label: 'Last Name',
})
}
// Add email field
const emailNode = store.addNode('text-control', store.rootNode.id)
store.updateNode(emailNode.id, {
propertyName: 'email',
label: 'Email',
schemaProperties: { type: 'string', format: 'email' },
})
// Add message field
const messageNode = store.addNode('text-control', store.rootNode.id)
store.updateNode(messageNode.id, {
propertyName: 'message',
label: 'Message',
schemaProperties: { type: 'string' },
options: { multi: true },
})Generated Output
JSON Schema:
json
{
"type": "object",
"properties": {
"firstName": { "type": "string", "minLength": 1 },
"lastName": { "type": "string" },
"email": { "type": "string", "format": "email" },
"message": { "type": "string" }
},
"required": ["firstName"]
}UI Schema:
json
{
"type": "VerticalLayout",
"elements": [
{
"type": "HorizontalLayout",
"elements": [
{ "type": "Control", "scope": "#/properties/firstName", "label": "First Name" },
{ "type": "Control", "scope": "#/properties/lastName", "label": "Last Name" }
]
},
{ "type": "Control", "scope": "#/properties/email", "label": "Email" },
{ "type": "Control", "scope": "#/properties/message", "label": "Message", "options": { "multi": true } }
]
}Tabbed Registration Form
Using Categorization and Category to create a tabbed form:
ts
const store = useBuilderStore()
// Replace root with a categorization
store.reset()
const cat = store.addNode('categorization', store.rootNode.id)
// Personal info tab
const tab1 = store.addNode('category', cat.id)
store.updateNode(tab1.id, { label: 'Personal Info' })
const name = store.addNode('text-control', tab1.id)
store.updateNode(name.id, {
propertyName: 'fullName',
label: 'Full Name',
schemaProperties: { type: 'string', minLength: 1 },
})
const dob = store.addNode('date-control', tab1.id)
store.updateNode(dob.id, {
propertyName: 'dateOfBirth',
label: 'Date of Birth',
})
// Preferences tab
const tab2 = store.addNode('category', cat.id)
store.updateNode(tab2.id, { label: 'Preferences' })
const newsletter = store.addNode('boolean-control', tab2.id)
store.updateNode(newsletter.id, {
propertyName: 'newsletter',
label: 'Subscribe to newsletter',
})
const theme = store.addNode('enum-control', tab2.id)
store.updateNode(theme.id, {
propertyName: 'theme',
label: 'Preferred Theme',
schemaProperties: { type: 'string', enum: ['light', 'dark', 'system'] },
})Conditional Form with Rules
Show or hide fields based on user input:
ts
const store = useBuilderStore()
// Employment status dropdown
const status = store.addNode('enum-control', store.rootNode.id)
store.updateNode(status.id, {
propertyName: 'employmentStatus',
label: 'Employment Status',
schemaProperties: {
type: 'string',
enum: ['employed', 'self-employed', 'unemployed', 'student'],
},
})
// Company name -- shown only when employed
const company = store.addNode('text-control', store.rootNode.id)
store.updateNode(company.id, {
propertyName: 'companyName',
label: 'Company Name',
rule: {
effect: 'SHOW',
condition: {
type: 'LEAF',
scope: '#/properties/employmentStatus',
expectedValue: 'employed',
},
},
})
// Business name -- shown only when self-employed
const business = store.addNode('text-control', store.rootNode.id)
store.updateNode(business.id, {
propertyName: 'businessName',
label: 'Business Name',
rule: {
effect: 'SHOW',
condition: {
type: 'LEAF',
scope: '#/properties/employmentStatus',
expectedValue: 'self-employed',
},
},
})
// University -- shown only when student
const university = store.addNode('text-control', store.rootNode.id)
store.updateNode(university.id, {
propertyName: 'university',
label: 'University',
rule: {
effect: 'SHOW',
condition: {
type: 'LEAF',
scope: '#/properties/employmentStatus',
expectedValue: 'student',
},
},
})Import and Edit Existing Schema
Load an existing form definition, modify it, and re-export:
ts
const store = useBuilderStore()
// Import existing schemas
const existingJsonSchema = {
type: 'object',
properties: {
name: { type: 'string' },
email: { type: 'string', format: 'email' },
},
}
const existingUiSchema = {
type: 'VerticalLayout',
elements: [
{ type: 'Control', scope: '#/properties/name', label: 'Name' },
{ type: 'Control', scope: '#/properties/email', label: 'Email' },
],
}
store.importSchema(existingJsonSchema, existingUiSchema)
// The tree now has two controls -- add a phone number field
const phone = store.addNode('text-control', store.rootNode.id)
store.updateNode(phone.id, {
propertyName: 'phone',
label: 'Phone Number',
schemaProperties: { type: 'string', pattern: '^\\+?[1-9]\\d{1,14}$' },
})
// Export the updated schemas
const { jsonSchema, uiSchema } = store.exportSchema()
console.log(JSON.stringify(jsonSchema, null, 2))
console.log(JSON.stringify(uiSchema, null, 2))Live Demos
Validation Before Export
Always validate before saving to catch structural issues:
ts
import { useBuilderStore, validateSchemaNode, validateGeneratedSchemas } from '@banclo/jsonforms-core'
const store = useBuilderStore()
function exportWithValidation() {
// Step 1: Validate the tree structure
const treeResult = validateSchemaNode(store.rootNode)
if (!treeResult.valid) {
alert('Please fix the following errors:\n' + treeResult.errors.map(e => e.message).join('\n'))
return null
}
// Step 2: Generate schemas
const { jsonSchema, uiSchema } = store.exportSchema()
// Step 3: Validate generated output
const schemaResult = validateGeneratedSchemas(jsonSchema, uiSchema)
if (!schemaResult.valid) {
console.error('Schema validation failed:', schemaResult.errors)
return null
}
// Step 4: Warn about non-critical issues
const allWarnings = [...treeResult.warnings, ...schemaResult.warnings]
if (allWarnings.length > 0) {
console.warn('Warnings:', allWarnings.map(w => w.message))
}
return { jsonSchema, uiSchema }
}