Making BPMN-IO do what it was not built to do

Enterprise business process management suite · Faash · 2024 to 2025

  • React
  • TypeScript
  • BPMN-IO
  • FormJS
  • XYFlow

Outcome

  • Custom workflow elements that participate correctly in undo, validation, and serialisation
  • No fork of the upstream library, so version upgrades stayed routine

The problem

BPMN-IO is a good library with a clear idea of what a business process looks like. Our customers had processes that did not fit that idea. The options were to fork the library and inherit its maintenance forever, to work around it in application code and end up with two competing models of the same diagram, or to learn its extension points properly and use them.

Most of the gaps were about where our users are. The product needed Jalali dates — not only a Persian calendar in the interface but the date logic behind it — and right-to-left layout throughout the modeller and the form builder. It also needed form fields that could react to one another, which meant exposing validation, onChange and the other input events as things a process designer could attach behaviour to rather than internals only the application code could reach.

What I did

I went with the third option. BPMN-IO exposes a module system and a command stack, and once you stop treating it as a black box you can add element types, override rendering, and hook into its behaviours without ever touching its source. That meant our custom nodes participated properly in undo and redo, in validation, and in serialisation, instead of being a layer painted on top that desynchronised the moment someone pressed Ctrl+Z.

The clearest example is a form-builder field whose value is a JavaScript function. A process designer writes a handler into the form definition and it is wired up as a real event handler on the rendered form, so changing field X can set, clear or re-validate field Y without anyone shipping application code for that particular form.

The same approach applied to FormJS for the form-building side, so that a process step and the form attached to it stayed consistent as either one changed.

Painted on topCustom nodedrawn over the canvasCtrl + Zdiagram revertsNode survivesmodel and view disagreeThrough the module systemCustom noderegistered as a moduleCtrl + Zcommand stack revertsNode reverts toomodel and XML agree
A custom element drawn on top of the canvas is not part of the diagram's history. Registered through the module system, the same element is undone, validated and serialised by the machinery that already handles every built-in one.

What I would do differently

Building on that open-source project at all. It was considerably larger than our requirements needed, and every feature we added was a negotiation with someone else’s architecture. Starting from scratch would have been more work in the first month and less work in every month after it.

← All work