Skip to content

Migration

Coming from Form.io

Migrating a form portfolio is usually the reason people put a migration off for a year. convertFormioForm exists to make it incremental: it converts what it can, reports what it cannot, and never throws — so you can run it over a hundred forms and get a work list instead of a stack trace.

  1. Feed it a Form.io form definition.

    migrate.ts
    import { convertFormioForm } from '@streamline-pulse/formkrafter-core'
    const formioJson = JSON.parse(await readFile('./legacy-form.json', 'utf8'))
    const { spec, warnings } = convertFormioForm(formioJson)
  2. Review the warnings. Unknown components are skipped and listed here rather than aborting the conversion.

    for (const warning of warnings) console.warn(warning)
    // e.g. 'button "submit" skipped — FormKrafter renders its own submit action'
  3. Store the resulting spec, or open it in the builder for a manual pass.

Form.io FormKrafter
Field components (textfield, email, number, day, select, radio, selectboxes, checkbox, textarea, phone, url, signature, address, …) The matching input bricks, inputMask included
Layout: panels, columns, tables, tabs, fieldsets Layout bricks
Wizard (display: "wizard") stepper with per-step validation
datagrid / editgrid data-grid with per-row validation
validate.* (required, min/max, lengths, pattern, custom JS) Validators; custom JS is auto-wrapped for the sandbox (data/row become dataMap)
conditional (show/when/eq) and JavaScript logic triggers Rules; a conditional takes precedence over an always-hidden flag
Select dataSrc: url / json Remote or static options (valuePropertyvalueKey, item templates → labelKey, dotted paths supported). Lists of plain strings or numbers convert to matching label/value pairs
File components (storage: url, multiple) file brick with uploadUrl / multiple configs
Nested form components nested-form bricks with specRef
Content / HTML components content bricks, conditionals preserved

The converter is regression-tested against real production Form.io forms — wizards, conditional file uploads, cascaded selects and custom validators.

Methodology: gzipped bytes a page actually downloads. FormKrafter v0.16.0 measured in headless Chromium against the published lazy-loading bundle, JS and CSS together; Form.io @formio/js v5.5.0 measured from its official CDN bundles (July 2026).

FormKrafter Form.io
Builder (renderer included) ~130 KB ~470 KB (formio.full.min.js)
Renderer only ~130 KB ~415 KB (formio.form.min.js)
Required CSS 1.6 KB, optional 129 KB + Bootstrap

The two FormKrafter rows are identical on purpose: the lazy-loading bundle ships its components in shared chunks, so a renderer-only page downloads the same set as one running the builder. What stays out until you open a rules or JS-options editor is the code editor, roughly 100 KB gzipped on its own.

Rendering cost stays flat where it matters. Measured on a real converted production form — a 5-step wizard of 93 bricks (nested layouts, file uploads, cascaded selects) — then on synthetic variants scaling it up to 911 bricks on a single page. Methodology: FormKrafter v0.6.0 production build, headless Chromium via Playwright, median of warm runs; keystroke latency is input-event-to-next-paint, so it includes up to two frames of scheduling.

Bricks on the page Mount validate() Keystroke latency (median / p90)
92 (the real form) 17 ms 0.6 ms 14 ms / 33 ms
456 29 ms 1.0 ms 14 ms / 29 ms
911 45 ms 1.9 ms 26 ms / 33 ms

The number worth noticing is the last column: typing latency does not grow with form size — a keystroke never pays for the bricks it does not touch, and even a 911-brick page stays within the 60 fps frame budget while typing.

Your stored definitions shrink too. Real Form.io exports carry heavy boilerplate — every component serialized with all its defaults — while the converter keeps only what carries meaning and compacts static option lists into a newline string whenever labels equal values. Measured on a portfolio of six production forms:

Raw JSON Gzipped
Form.io definitions 277 KB 40 KB
Converted FormKrafter specs 66 KB (3–5.5× smaller per form) 18 KB

For the biggest lists — professions, nationalities — do not embed them at all. The converter warns past 100 static options and recommends:

{ "optionsSource": "remote", "optionsUrl": "/api/professions?q={search}" }

The select queries your API, with search-as-you-type support.

Form.io executes custom validators and logic with real JavaScript. FormKrafter runs the converted code in a CSP-safe AST interpreter — see dynamic rules. Converted snippets keep working because the converter injects compatibility aliases (const data = dataMap; const row = dataMap;), and they gain the sandbox’s isolation for free.

A project by Streamline Pulse