Forms evolve. You add fields, remove fields, rename fields, restructure layouts. In most form systems, this creates a problem: what happens to submissions made against the old version of the form? Either the old data becomes orphaned, or the new form cannot properly display historical submissions because the schema no longer matches.
Form.io solves this with Form Revisions. Each time you publish changes to a form, the platform stores the complete form JSON schema as a numbered revision. Submissions are tagged with the revision number against which they were captured. When you view a submission, you can choose whether to render it against the current form schema or against the original schema that existed when the submission was made.
This is not just a “history” feature. It is fundamental to how schema-driven form infrastructure must work in production systems where data integrity matters.
How Form Revisions Work Internally
When you enable Form Revisions on a form, Form.io creates a parallel storage structure. The base form object contains non-versioned metadata: title, path, permissions, and a reference to the “current” revision. A separate collection stores complete copies of the form’s components array for each revision.
Every time you click “Publish Form,” the system:
- Creates a new revision record containing the complete component schema
- Increments the Vid (Version ID) number
- Updates the base form to point to this new revision as “current”
- Records who made the change, when, and any revision notes
The revision stores the entire components array, not a diff. This means each revision is self-contained and can be rendered independently without needing to reconstruct state from a sequence of changes. The trade-off is storage space, but the benefit is deterministic rendering of any historical form state.
Submissions made after enabling revisions include a _fvid (form version ID) property that references the revision under which they were captured:
{
"_id": "657b9e7b4001c82890626a5e",
"_fvid": 2,
"data": {
"firstName": "Jane",
"lastName": "Doe",
"middleName": "Marie"
},
"form": "654abc123def456789012345",
"created": "2024-01-15T09:30:00.000Z"
}
This _fvid value determines which schema the renderer uses when displaying the submission.
The Two Rendering Modes
Form.io provides two options when enabling revisions, and the choice matters significantly for your application’s behavior:
“Enabled: Use the current form version when viewing a submission” always renders submissions against the latest form schema. If you added a field in revision 3 that did not exist in revision 1, submissions from revision 1 will show that field as empty. If you removed a field, data captured in that field will not display (though it remains in the database).
“Enabled: Use the original form version when viewing a submission” renders each submission against the schema that existed when it was captured. A submission from revision 1 displays with the revision 1 form layout. A submission from revision 3 displays with the revision 3 layout. This preserves the exact user experience that existed at capture time.
For compliance and audit purposes, “use original version” is typically the correct choice. It ensures that what you see when reviewing a historical submission matches what the user saw when they filled out the form. For operational dashboards where you want consistent column layouts regardless of when data was captured, “use current version” may be preferable.
The API supports explicit version retrieval. To render a specific submission against a specific form revision:
GET /myproject/myform/submission/657b9e7b4001c82890626a5e/v/2
This returns the submission with form context from revision 2, regardless of the project’s default rendering mode.
What Revisions Track (and What They Do Not)
Form Revisions version the components array. This includes all field definitions, validation rules, conditional logic, calculated values, and layout structures. When you restore an old revision, you get back the exact component configuration from that point in time.
Revisions do not version:
- Form-level permissions (who can submit, who can read submissions), but see Teams & Permissions
- Actions (webhooks, email notifications, save-to-resource)
- Project settings
- Submission data itself
This means if you change form permissions between revisions, all submissions inherit the current permission configuration. You cannot restore a previous permission state by restoring an old revision.
The Data Visibility Problem
When you remove a field from a form, submissions that included that field still contain the data. The data exists in MongoDB. But if you’re using “current version” rendering, that data will not display in the portal’s Data tab or in rendered forms.
Consider this scenario:
- Revision 1 has fields A, B
- Revision 2 adds field C (now A, B, C)
- Revision 3 removes field B (now A, C)
If you look at the Data tab with revision 3 active, submissions from revision 1 will show columns for A and C. Column B is missing from the display because field B no longer exists in the current schema. The Banana1, Banana2 values from those old submissions are still in the database, but they are invisible in the current UI.
The fix is the Restore action. Restoring revision 2 creates a new revision (4) that clones revision 2’s component schema. It does not overwrite revision 3. Now field B exists in the current schema, and the Data tab displays all historical B values. The revision history shows: 1 → 2 → 3 → 4 (restored from 2).
This is important: restoring never destroys history. It always creates a new revision that copies an old configuration.
Draft Mode
Form Revisions support a draft workflow. You can save changes to a form without publishing, which creates a draft state. The draft:
- Does not increment the Vid
- Does not affect what submissions are captured against
- Does not appear in the Revisions tab as a numbered entry
- Can be edited multiple times before publishing
- Is replaced (not versioned) with each save
Only one draft can exist per form. Publishing the draft converts it into a numbered revision. Discarding changes removes the draft and reverts to the last published revision.
This workflow enables testing form changes in isolation before pushing them live. Combined with Form.io’s Stages system, you can draft changes in a development stage, publish within that stage to test, then deploy the versioned stage to production.
Revisions and Related Features
Form Revisions interact with several other Form.io capabilities:
Form Change Logs track field-level changes within submissions. Revisions track form schema changes. These are complementary: revisions tell you the form structure changed, change logs tell you individual submission values changed.
Submission Revisions (different from Form Revisions) track edits to individual submissions over time. When a user updates their submission, Submission Revisions preserve the previous state. Form Revisions preserve the form structure; Submission Revisions preserve the data history.
Export Form Data respects the revision context. When exporting submissions from a specific revision’s Data view, only submissions captured against that revision are included. When exporting from the main Data tab, all submissions export, but columns reflect the current schema.
PDF Forms can include a Submission Revision Log that shows the change history for individual submissions. This is separate from Form Revisions but often used together for audit trails.
Multi-Tenancy deployments maintain independent revision histories per tenant. Each tenant’s forms version independently.
When Not to Use Revisions
Form Revisions add overhead. Every publish creates a complete copy of the component schema. For forms that change frequently and have large component arrays, this accumulates storage.
Do not enable revisions if:
- Your form is in rapid prototyping phase and will change daily
- You do not need to render historical submissions against their original schema
- You are using Form.io purely for transient data capture where submissions are processed and discarded
- Storage constraints are severe
Consider enabling revisions when:
- The form is in production with real user submissions
- Compliance requires audit trail of form changes
- Historical submissions must render accurately for legal or operational review
- Multiple team members edit forms and you need accountability for who changed what
Revisions are part of the Security Module. Contact Form.io for licensing if you are self-hosting.
API Access to Revisions
The Form.io API exposes revision data through several endpoints documented in the API documentation:
Get all revisions for a form:
GET /myproject/myform/v
Get a specific revision’s schema:
GET /myproject/myform/v/2
Get a submission rendered with a specific revision:
GET /myproject/myform/submission/{submissionId}/v/2
These endpoints enable programmatic access for building custom admin interfaces, migration tools, or audit reports that need to compare form states across time.
The Relationship Between Form Schema and Submission Schema
Form.io maintains complete separation between form JSON and submission JSON. The form schema defines structure and behavior. The submission schema contains captured data. They connect through component key values: each component’s key in the form schema corresponds to a property name in the submission’s data object.
This separation is why revisions work. The submission stores raw data with key names. The form schema stores the context for interpreting that data (labels, validation rules, display logic). You can render the same submission data through different form schemas and get different presentations.
For a deeper explanation, see Form Schemas vs Submissions and JSON Schema vs Form.io Schema.
Implementation Notes
The Form.io changelog documents several fixes related to form revisions across server versions. Notable behaviors:
- Validation uses the form revision specified in the submission, not the current revision, when “use original version” is enabled
- Importing forms with revisions enabled preserves the
formRevisionproperty - The
full=truequery parameter ensures revision metadata loads correctly with form data
If you encounter unexpected revision behavior, check that your server version includes relevant fixes and that the revision setting matches your rendering expectations.
Related Resources:
