Every form in Form.io is a JSON document. Not HTML. Not a proprietary binary format. Not a database record with opaque fields. A readable, portable, version-controllable JSON schema that completely defines how the form looks, behaves, validates, and submits data.
This architecture is not an implementation detail. It is the foundation that makes Form.io work as forms infrastructure rather than a form builder application. When forms are JSON, they can be generated programmatically, stored in version control, migrated between environments, rendered by any compatible client, and processed by any system that understands the schema. The form becomes data, not code.
The Form JSON Schema
A Form.io form schema starts with a definition object that describes the form itself:
{
"title": "Employee Registration",
"name": "employeeRegistration",
"path": "employee/register",
"type": "form",
"display": "wizard",
"components": [...]
}
The title provides a human-readable name. The name serves as a camelCase API identifier. The path determines the URL endpoint where the form lives relative to your project base URL. The type distinguishes between forms (unstructured data collection) and resources (structured data objects that other forms can reference). The display property controls whether the form renders as a single page, a multi-step wizard, or a PDF form.
The components array contains the actual form fields. Each component is itself a JSON object with properties defining its type, behavior, validation rules, and display characteristics.
Component Schema Structure
Every form component shares a common schema structure. A simple text field looks like this:
{
"type": "textfield",
"key": "firstName",
"label": "First Name",
"placeholder": "Enter your first name",
"input": true,
"validate": {
"required": true,
"minLength": 2,
"maxLength": 50
}
}
The type property determines which component renders. Form.io includes dozens of built-in component types: text fields, text areas, numbers, emails, phone numbers, dates, checkboxes, radio buttons, select dropdowns, file uploads, signatures, and more. The component documentation on GitHub provides the complete schema specification for each type.
The key property is critical. It determines the property name in the submission data object. If you set key to firstName, the submission data will contain { "firstName": "whatever the user entered" }.
The validate object contains validation rules that apply both client-side (in the browser) and server-side (on submission). Both validation layers derive from the same schema, ensuring consistent behavior.
Conditional Logic in JSON
Form.io schemas support conditional logic that controls when components appear, what values they accept, and how they behave based on other form data.
Simple conditionals use the conditional property:
{
"type": "textfield",
"key": "spouseName",
"label": "Spouse Name",
"conditional": {
"show": true,
"when": "maritalStatus",
"eq": "married"
}
}
This field only appears when the maritalStatus field equals “married”. The renderer evaluates these conditions in real time as users interact with the form.
For complex conditions, the schema supports JSON Logic, a portable format for expressing arbitrary boolean logic:
{
"conditional": {
"json": {
"and": [
{ "===": [{ "var": "data.country" }, "US"] },
{ ">=": [{ "var": "data.age" }, 21] }
]
}
}
}
This approach keeps all form logic within the schema rather than scattered across application code. When you export a form or migrate it between environments, the conditional logic travels with it.
From Schema to Rendered Form
The Form.io renderer, available as an open-source JavaScript library, transforms JSON schemas into interactive HTML forms. The rendering process happens entirely client-side.
Formio.createForm(document.getElementById('formio'), {
components: [
{
type: 'textfield',
key: 'firstName',
label: 'First Name',
input: true
},
{
type: 'button',
action: 'submit',
label: 'Submit',
theme: 'primary'
}
]
});
You can pass the schema directly as a JavaScript object, or you can pass a URL that returns the schema:
Formio.createForm(
document.getElementById('formio'),
'https://myproject.form.io/employee'
);
The second approach enables decoupled form updates. Your application code contains only a reference to the form. The actual schema lives on the Form.io server and can change without touching your application.
The renderer handles all the complexity of turning JSON into functional HTML: creating input elements, attaching event listeners, managing state, evaluating conditionals, running validation, formatting values, and submitting data. Your application receives a working form without building any of this infrastructure.
Automatic API Generation
When you create a form in Form.io, the platform automatically generates REST API endpoints to support that form. The same JSON schema that defines the form structure also defines the API structure.
For a form at path employee, the platform creates:
GET /employeereturns the form schemaPOST /employee/submissioncreates a new submissionGET /employee/submissionlists submissionsGET /employee/submission/:idretrieves a specific submissionPUT /employee/submission/:idupdates a submissionDELETE /employee/submission/:iddeletes a submission
The submission API expects and returns JSON that matches the schema structure. If your form has fields with keys firstName, lastName, and email, the submission data looks like:
{
"data": {
"firstName": "Sarah",
"lastName": "Chen",
"email": "sarah@example.com"
}
}
The API validates incoming data against the schema before accepting it. Required fields must be present. Pattern validations must pass. Type constraints must match. This server-side validation uses the same schema the client used, ensuring consistent behavior.
The API documentation covers the complete endpoint specification, authentication, filtering, pagination, and advanced query capabilities.
Form.io Schema vs. JSON Schema Standard
Form.io’s schema format is distinct from the JSON Schema specification (the IETF draft standard for describing JSON documents). This is intentional.
JSON Schema was designed for data validation. It describes what valid JSON documents look like: which properties exist, what types they have, what values are acceptable. It does not describe how to render a form or how to present those fields to users.
Form.io’s schema describes both the data structure and the user interface. A single schema contains everything needed to render an interactive form, validate user input, and process submissions. This eliminates the need for separate UI definitions and data definitions that must stay synchronized.
Some libraries like jsonforms.io use standard JSON Schema for data definition and add a separate UI Schema for rendering. This works, but requires maintaining two coordinated schemas. Form.io’s unified approach trades standards compliance for practical simplicity: one schema, one source of truth.
If you need to generate JSON Schema from a Form.io form for integration with other systems, you can transform the component definitions programmatically. The component key becomes a property name, the type maps to JSON Schema types, and validation rules translate to JSON Schema constraints.
Working with Form JSON Programmatically
Because forms are JSON, you can manipulate them with standard tools and techniques.
Version control works naturally. Store form schemas in Git alongside your application code. Track changes, review diffs, branch and merge. When form definitions are text files, your existing development workflow applies.
Programmatic generation is straightforward. If you need to create forms dynamically based on database schemas, configuration files, or other metadata, you generate JSON objects. No special APIs required.
function generateFormFromFields(fields) {
return {
title: 'Dynamic Form',
components: fields.map(field => ({
type: field.type || 'textfield',
key: field.name,
label: field.label,
validate: { required: field.required }
}))
};
}
Import and export require no special tools. The Form.io portal lets you download form JSON and upload it to other projects. You can use the CLI for automated migration. Or you can simply copy JSON files between systems.
Testing becomes data comparison. To verify a form renders correctly, compare the actual rendered output against expected behavior for given schema input. Unit tests can validate schema transformations without rendering anything.
The Submission Data Model
Form.io maintains strict separation between form schemas and submission data. A form schema describes structure. A submission contains actual values.
Submission JSON follows a consistent format:
{
"_id": "507f1f77bcf86cd799439011",
"data": {
"firstName": "Sarah",
"lastName": "Chen",
"addresses": [
{ "type": "home", "street": "123 Main St" },
{ "type": "work", "street": "456 Office Park" }
]
},
"form": "507f1f77bcf86cd799439012",
"created": "2024-01-15T14:32:00.000Z",
"modified": "2024-01-15T14:32:00.000Z"
}
The data object contains the actual field values, structured according to component keys. The form reference links the submission to its form schema. Metadata fields track creation and modification times, ownership, and state.
This separation means you can change a form schema without invalidating existing submissions. Add a field, and old submissions simply lack that property. Remove a field, and existing data remains in the database even though new submissions will not include it. Form revisions track these schema changes over time.
When JSON Architecture Matters
The JSON-driven approach provides specific advantages for specific use cases.
Multi-environment deployments benefit from portable schemas. Export from development, import to staging, promote to production. The form is the same JSON in each environment; only the data differs.
Multi-tenant applications benefit from schema-per-tenant models. Each tenant can have customized forms stored as JSON documents, all rendered by the same application code.
Audit and compliance requirements benefit from readable definitions. Reviewers can examine exactly what a form collects without accessing a running system. The schema is the specification.
Integration projects benefit from standard formats. JSON parses in every language. Other systems can read form definitions, generate compatible submissions, or transform data for downstream processing.
Limitations of the JSON Approach
JSON schemas add a layer of abstraction. That abstraction has costs.
Complex custom components require code. While the schema can configure built-in components extensively, truly custom rendering logic requires custom component development. The component implementation lives in JavaScript, not JSON.
Schema size grows with form complexity. A form with hundreds of fields produces a large JSON document. The renderer handles this efficiently, but extremely large schemas may impact initial load time.
Debugging requires understanding the schema. When forms behave unexpectedly, you debug JSON configuration, not visual properties. This requires familiarity with the schema structure.
Schema validation happens at runtime. Invalid schemas produce runtime errors rather than compile-time errors. The form builder prevents most issues, but programmatically generated schemas need careful validation.
Related Resources
- Form JSON Schema specification provides the complete schema reference
- Components JSON Schema documents individual component properties
- Form.io JavaScript renderer source code and documentation
- API Documentation covers REST endpoints for forms and submissions
- Form Validation and Conditional Logic explains validation schema properties
- Decoupled Form Updates describes the runtime rendering model
- Form Revisions covers schema versioning
