Renderable Subset
Understand what Schemaform can render, how to check imported schemas, and how validation stays broader than rendering.
JSON Schema is a validation language. Some constructs describe valid data but do not have a clear form UI: not, arbitrary allOf merges, patternProperties, conditional branches, and $ref graphs are useful to validators but ambiguous for a Form Renderer.
Schemaform therefore promises rendering for a documented Renderable Subset. The Form Builder only emits schemas inside that subset, so builder-created forms render. Imported hand-written schemas should run through the Renderability Check before you attach a UI Schema.
v1 subset
The v1 Renderable Subset is intentionally practical:
| JSON Schema construct | Rendered as |
|---|---|
type: "object" with properties | A form data object |
type: "string" | Text input by default |
format: "email" | Email input and email validation |
format: "date" | Date widget when the renderer provides one |
type: "number" / type: "integer" | Numeric input |
type: "boolean" | Checkbox or switch-style widget |
enum | Select-style widget |
type: "array" with object or scalar items | Repeater when paired with a UI Schema Array node |
required, minLength, maxLength, minimum, maximum, minItems, maxItems | Validation constraints |
The UI Schema still decides presentation: Pages, Groups, Columns, Controls, Array row layouts, widgets, navigation mode, and error reveal timing.
Unsupported for rendering
The Renderability Check flags constructs outside the v1 subset, including:
$refallOf,anyOf,oneOfnotif,then,elsepatternPropertiesdependentRequired,dependentSchemascontainsprefixItemsunevaluatedProperties,unevaluatedItems- polymorphic
typearrays such as["string", "null"]
Validation remains broader than rendering. Ajv can validate full JSON Schema 2020-12, even when a schema includes constructs the Form Renderer cannot turn into Controls.
Check renderability
Use checkRenderability from Core when accepting an imported schema.
import { checkRenderability, type JsonSchema } from "@mackehansson/schemaform";
const importedSchema = {
type: "object",
properties: {
email: {
type: "string",
format: "email",
},
metadata: {
patternProperties: {
"^x-": { type: "string" },
},
},
},
} satisfies JsonSchema;
const issues = checkRenderability(importedSchema);
if (issues.length > 0) {
console.table(
issues.map((issue) => ({
pointer: issue.pointer,
keyword: issue.keyword,
message: issue.message,
})),
);
}Each issue includes a JSON Pointer to the unsupported construct. Use that pointer to show actionable import feedback instead of a vague "schema not supported" message.
Importing an existing JSON Schema
An import flow usually has three steps:
import {
checkRenderability,
type FormDefinition,
type JsonSchema,
} from "@mackehansson/schemaform";
export function importJsonSchema(schema: JsonSchema): FormDefinition {
const issues = checkRenderability(schema);
if (issues.length > 0) {
throw new Error(
issues.map((issue) => `${issue.pointer}: ${issue.message}`).join("\n"),
);
}
return {
formatVersion: 1,
schema,
uiSchema: {
pages: [
{
type: "Page",
title: "Imported form",
children: Object.keys(schema.properties ?? {}).map((name) => ({
type: "Control",
scope: `#/properties/${name}`,
})),
},
],
},
rules: [],
};
}That generated UI Schema is only a starting point. A real importer should escape JSON Pointer segments, choose widgets from formats and enums, and let a developer or admin refine Pages, Groups, Columns, and Rules in the Form Builder.
Validation after import
Do not use the Renderability Check as your data validator. It answers "can Schemaform render this schema as a form?" The Validator answers "does this Working State satisfy the JSON Schema?"
import { validate } from "@mackehansson/schemaform";
const errors = validate(importedSchema, currentWorkingState);Keeping those questions separate lets Schemaform render a practical subset while still using JSON Schema 2020-12 as the data-and-validation layer.