Living Document Notice
Published 2026-09-15. The evolving architecture, revisions, and connected notes for this dispatch live in the Stax Digital Garden.
Schema-Driven UI Generation
Summary
Building dedicated frontend forms for every note type and metadata property leads to sprawling UI codebases. As knowledge collections diversify into project tracking, literature reviews, and contact indexes, client applications frequently require custom React components, bespoke validation handlers, and specialized REST endpoints for each schema iteration.
FreeNext removes bespoke UI glue code by driving client interface generation directly from declarative JSON Schema specifications. The frontend rendering engine inspects the schema contract of a document and generates corresponding input widgets, validation rules, and layout structures at runtime.
Dynamic Component Generation Pipeline
When a user opens a note or configures a template, the FreeNext lens evaluates the schema definition embedded in the vault configuration. The schema serves as the sole contract between storage and layout.
+-------------------------------------------------------------+
| Declarative Document Schema |
| (JSON Schema / TypeBox Spec) |
+-------------------------------------------------------------+
|
| Ingest & AST Parse
v
+-------------------------------------------------------------+
| Schema Resolver & Validator |
| (Validates Frontmatter Values Against Spec) |
+-------------------------------------------------------------+
|
| Component AST Stream
v
+-------------------------------------------------------------+
| Dynamic Form Renderer |
| [Text Input] [Date Picker] [Tag Multi-Select] |
+-------------------------------------------------------------+
|
| Emits Synchronized Patch
v
+-------------------------------------------------------------+
| CommonMark Frontmatter Serializer |
+-------------------------------------------------------------+
Because component rendering is algorithmic, introducing a new field type requires updating a single JSON schema definition rather than deploying new client application builds.
Schema to Interface Component Mapping
The rendering engine maps standard JSON Schema primitive types and semantic constraints directly into interactive interface controls.
| Schema Primitive Type | Format / Constraint | Rendered UI Control | Validation Execution |
|---|---|---|---|
string | format: "date" | Native Date Picker Widget | ISO 8601 validation regex |
string | enum: [...] | Single-Select Dropdown | Member inclusion check |
array | items: { type: "string" } | Chip Tag Multi-Input | Array element uniqueness |
integer | minimum: 0, maximum: 100 | Stepper / Slider Input | Range boundary assertion |
boolean | default | Toggle Switch | Strict boolean truthiness |
object | nested properties | Collapsible Fieldset Panel | Recursive schema evaluator |
This deterministic mapping ensures consistent interaction patterns across diverse document types while keeping validation logic synchronized between disk storage and form fields.
Declarative Spec and Renderer Implementation
The following code illustrates a note property schema and the lightweight JavaScript generator that maps it to native Web Components:
// schema.ts: Declarative Note Property Schema
export interface NotePropertySchema {
$id: string;
title: string;
type: 'object';
properties: Record<string, SchemaProperty>;
required?: string[];
}
export interface SchemaProperty {
type: 'string' | 'number' | 'boolean' | 'array';
title: string;
description?: string;
enum?: string[];
format?: string;
items?: { type: string };
}
export const DispatchSchema: NotePropertySchema = {
$id: 'https://bosunpkm.com/schemas/dispatch.json',
title: 'Dispatch Note Contract',
type: 'object',
properties: {
title: { type: 'string', title: 'Dispatch Title' },
date: { type: 'string', format: 'date', title: 'Publication Date' },
draft: { type: 'boolean', title: 'Draft Status' },
tags: {
type: 'array',
title: 'Classification Tags',
items: { type: 'string' },
},
},
required: ['title', 'date', 'draft'],
};
// renderer.ts: Web Component Form Builder
export class SchemaFormBuilder {
buildForm(
schema: NotePropertySchema,
initialValues: Record<string, unknown>,
onChange: (values: Record<string, unknown>) => void
): HTMLElement {
const container = document.createElement('form');
container.className = 'schema-form-root';
const state = { ...initialValues };
for (const [key, prop] of Object.entries(schema.properties)) {
const fieldWrapper = document.createElement('div');
fieldWrapper.className = 'field-row';
const label = document.createElement('label');
label.textContent = prop.title || key;
label.htmlFor = `field-${key}`;
fieldWrapper.appendChild(label);
if (prop.type === 'boolean') {
const input = document.createElement('input');
input.type = 'checkbox';
input.id = `field-${key}`;
input.checked = Boolean(state[key]);
input.addEventListener('change', () => {
state[key] = input.checked;
onChange(state);
});
fieldWrapper.appendChild(input);
} else if (prop.type === 'string' && prop.format === 'date') {
const input = document.createElement('input');
input.type = 'date';
input.id = `field-${key}`;
input.value = String(state[key] || '');
input.addEventListener('input', () => {
state[key] = input.value;
onChange(state);
});
fieldWrapper.appendChild(input);
} else {
const input = document.createElement('input');
input.type = 'text';
input.id = `field-${key}`;
input.value = String(state[key] || '');
input.addEventListener('input', () => {
state[key] = input.value;
onChange(state);
});
fieldWrapper.appendChild(input);
}
container.appendChild(fieldWrapper);
}
return container;
}
}By decoupling interface controls from hardcoded application components, schema-driven generation keeps frontmatter structures flexible and user-defined.
- Directus Target: freenext
- Garden Source Reference: NXT-1001 - The Application Is Just a Lens, REC-1001 - Recipe Schema Engine, MOC - Data Liberation Workbenches, MOC - The Plain-Text Longevity Standard, MOC - Local-First Systems and Synchronization, MOC - Bosun PKM Tools