npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@uubato/uubato-bpmn-modeller

v0.6.1

Published

A React component for building BPMN 2.0 diagrams in the browser, built on [maxGraph](https://github.com/maxGraph/maxGraph). Supports the full standard BPMN 2.0 element set plus a broad range of execution-oriented properties (async continuations, job retry

Readme

BPMN Modeler

A React component for building BPMN 2.0 diagrams in the browser, built on maxGraph. Supports the full standard BPMN 2.0 element set plus a broad range of execution-oriented properties (async continuations, job retry/priority, external task workers, DMN business-rule references, connector configuration), exported via a custom XML extension namespace rather than tied to any specific workflow engine.

This package lives at BPMN/ in the bpmn-modeler monorepo, which also holds Browser/ (the dev/demo harness for this package), DMN/, and CMMN/ (placeholders for future DMN/CMMN modeling packages).

BpmnModeler is a persistence-free, controlled component: you give it xml and get xml back via onChange. It has no import/export UI, no "open recent" list, and no storage of its own — where diagrams live (a server, localStorage, a filesystem) is entirely up to the app embedding it.

Published on npm as @uubato/uubato-bpmn-modeller. See docs/npm-package-migration.md / docs/npm-package-migration-status.md for the package-extraction history, or Developing against source below if you want to work against this monorepo's checkout directly instead of the published package.

Installation

npm install @uubato/uubato-bpmn-modeller

Peer dependencies (install alongside it):

npm install [email protected] [email protected]

Usage

import { useState } from 'react'
import { BpmnModeler } from '@uubato/uubato-bpmn-modeller'
import '@uubato/uubato-bpmn-modeller/style.css'

function DiagramEditor() {
  const [xml, setXml] = useState(null) // null = start with a blank canvas

  return (
    <BpmnModeler
      xml={xml}
      onChange={setXml}
      theme="light"
    />
  )
}

The package has no default export — everything is a named export (BpmnModeler, createEmptyBpmnXml). This keeps import/require behavior identical across every consumption path (ESM, CJS, bundler interop), with no .default unwrapping needed anywhere.

@uubato/uubato-bpmn-modeller/style.css includes both the component's own layout rules and the color/shadow custom properties (--color-*, --shadow-*, …) they rely on — that's the only stylesheet you need to import. box-sizing: border-box is scoped to the component's own subtree, so it doesn't affect the rest of your page. <BpmnModeler> fills whatever height its container gives it — same contract as any full-size embedded widget — so make sure the element it renders into has a definite height, e.g.:

<div style={{ height: '100vh' }}>
  <BpmnModeler xml={xml} onChange={setXml} />
</div>

It does not include a page-wide reset (html/body/#root margins, a default font-family) — that's left to your own app, the same way any other embedded component wouldn't rewrite your whole page's global styles.

Props

| Prop | Type | Description | |---|---|---| | xml | string \| null \| undefined | The diagram's BPMN XML. The component is controlled: null/undefined means "leave the current diagram alone" (no import happens). Setting it to a new string imports it. | | onChange | (xml: string) => void | Called with the freshly-exported XML on every graph or process-data mutation. No built-in debounce — debounce/persist on your side if needed. | | theme | 'light' \| 'dark' | Defaults to 'light'. Plain controlled prop — the component repaints to match it but owns no toggle UI and no persistence; there's no onThemeChange. | | readOnly | boolean | Defaults to false. Disables every user-driven mutation — toolbar, canvas edits (move/resize/delete/connect/inline-rename), context menu, properties/documentation panel, XML view editing, and undo/redo. Selection, pan, zoom, and the xml/onChange controlled data flow keep working — the host can still swap diagrams while read-only. | | availableDecisions | Array<{ id: string, name: string }> | Defaults to []. When non-empty, the business-rule-task Properties panel's Decision Ref field becomes a <select> populated from this list instead of a free-text input — for a host that also embeds @uubato/uubato-dmn-modeller and wants to resolve decisionRef against real, currently-open DMN decisions rather than a typed key. Pair with that package's listDecisions(xml) helper to derive the list from a DMN/DRD document's XML. | | showPropertiesPanel | boolean | Defaults to true. Set to false to remove the right-hand panel (ElementPanel/ProcessPanel) and its collapse toggle from the UI entirely — for a stripped-down, canvas-only embed. Distinct from readOnly, which keeps the panel visible but locks its fields; this hides it altogether. |

createEmptyBpmnXml()

Also exported from the package root. Returns a well-formed, empty-process BPMN XML string with a freshly generated id — pass it as xml to reset the diagram to a blank canvas (a "New" action):

import { BpmnModeler, createEmptyBpmnXml } from '@uubato/uubato-bpmn-modeller'

// ...
<button onClick={() => setXml(createEmptyBpmnXml())}>New</button>

What the host app owns

BpmnModeler only renders what's directly involved in modeling the one open diagram (toolbar, canvas, properties panel, validation, undo/redo, the live XML view). Everything else is left to you:

  • Starting a new diagram, an "Open Recent" list, file import/export buttons, and any auto-save/persistence loop
  • The theme toggle UI (and remembering the user's choice)
  • The read-only toggle UI, if you want one — like theme, readOnly is a plain controlled prop with no toggle of its own; you decide when and how a user (or your own app logic) flips it
  • A keyboard-shortcuts trigger (ShortcutOverlay is exported from the package root if you want to reuse it — import { ShortcutOverlay } from '@uubato/uubato-bpmn-modeller' — but nothing wires it up automatically)

Advanced usage

Most consumers only need the props table above. Hosts that need to trigger undo/redo, zoom, fit-to-screen, or the XML/Validate panels from outside a specific <BpmnModeler> instance — e.g. wiring a native OS menu in an Electron app with several open tabs — can inject a store created via createModelerStore() through an _internalStore prop, then read/write it from sibling code with zustand's own useStore(store, selector):

import { useStore } from 'zustand'
import { BpmnModeler, createModelerStore } from '@uubato/uubato-bpmn-modeller'

const store = createModelerStore() // one per open diagram/tab
// <BpmnModeler xml={xml} onChange={onChange} _internalStore={store} />
// elsewhere, outside that component's subtree:
const undoManager = useStore(store, (s) => s.undoManager)

createModelerStore, ModelerStoreContext, and useModelerStore are all exported from the package root — the latter two only matter if you're building components that live inside the <BpmnModeler> subtree a given store was provided to (useModelerStore reads from React context; sibling code outside that subtree needs the raw useStore(store, selector) form above instead).

Two more exports round out this tier, for hosts doing their own rendering around a <BpmnModeler> instance:

  • updateGraphTheme(graph, isDark) — re-applies every registered cell style's icon/color set after a theme change, without re-registering the whole stylesheet. <BpmnModeler> already calls this internally when its own theme prop changes; exported for a host that reaches a specific instance's graph via _internalStore and needs to trigger the same repaint from outside.
  • getTypeName(cellValue) — returns a cell's human-readable BPMN type name (e.g. "Service Task", "Exclusive Gateway") from its raw cellValue object. Useful for building your own export/print view (a PDF property table, say) without duplicating the type-name logic PropertiesPanel and ContextMenu already use internally.

See the project's CLAUDE.md ("Advanced / low-level API" section) for the full mechanism and an in-repo example (Browser/src/App.jsx + DemoControls.jsx).

Browser/ in this monorepo is exactly that: a reference host implementation, useful as a template if you're building your own — including the "🔓 Editable / 🔒 Read-only" button in DemoControls.jsx that demonstrates exactly this.

Developing against source

If you're working inside this monorepo (or a checkout/symlink of it) and want to iterate on BPMN/src without going through a published version, you can import the component by relative path instead of npm installing it:

import { useState } from 'react'
import BpmnModeler from '<relative-path>/bpmn-modeler/BPMN/src/BpmnModeler.jsx'
// No separate CSS import needed — BpmnModeler.jsx imports its own styles
// (src/styles/tokens.css) directly.

function DiagramEditor({ initialXml, onDiagramSaved, theme }) {
  const [xml, setXml] = useState(initialXml)

  return (
    <BpmnModeler
      xml={xml}
      theme={theme}
      onChange={(nextXml) => {
        setXml(nextXml)
        onDiagramSaved(nextXml) // caller owns debouncing/persistence entirely
      }}
    />
  )
}

See docs/npm-package-migration-status.md for more detail.


Contributing / local development

Prerequisites

  • Node.js v18 or later
  • npm v9 or later

Setup

Run from the repo root (this is an npm workspace, so a root install wires up both BPMN/ and Browser/):

npm install

Development

There's no standalone dev server for this package alone — it's developed against the Browser/ demo app, which embeds BpmnModeler the same way an external consumer would (via the workspace dependency, aliased to this package's source for instant hot-reload). From the repo root:

npm run dev

Testing

Run this package's unit tests (Vitest):

npm test

(equivalent to npm test -w BPMN from the repo root, or plain npm test from inside BPMN/). End-to-end tests live in Browser/ since they exercise the running demo app — see that package's README.

A pre-push git hook (managed by Husky, installed automatically via npm install at the repo root) runs npm run test:all before every git push and blocks the push if any test fails. To push despite a failure use git push --no-verify.

Library build

Build the installable package (ESM + CJS + CSS) to dist-lib/:

npm run build:lib

This is what npm publish would ship — see the exports field in package.json and docs/npm-package-migration.md for how it's wired up.

CI / publishing

This repo is GitLab-hosted; the .github/workflows/ci.yml/publish.yml files at the repo root are leftover GitHub Actions syntax that never actually ran here. Real CI lives in .gitlab-ci.yml (repo root): a unit_tests/e2e_tests job pair runs on every push/MR, plus a tag-triggered (v*.*.*) publish job — verifies the tag matches this package's package.json version, builds the library, previews the tarball with npm pack --dry-run -w BPMN, and publishes with npm provenance (npm publish -w BPMN --provenance) — and a manual publish_dry_run job for exercising the pipeline without a real tag push.

Automated publishing isn't functional yet: it needs a protected NPM_TOKEN CI/CD variable and protected v*/dmn-v* tag patterns in the GitLab project settings, neither of which is set up. Until then, releases (this package's first publish included) go out via a manual npm publish --access public run from BPMN/. See docs/npm-package-migration.md for the full history.

Project Structure

BPMN/
├── package.json            # the published package: name/exports/files/peerDeps
├── vite.config.js          # library build only (dist-lib)
├── vitest.config.js
├── src/
│   ├── BpmnModeler.jsx      # Package root component — re-exported by name (no default export) from index.js
│   ├── BpmnModeler.module.css
│   ├── index.js             # Package entry point — exports { BpmnModeler, createEmptyBpmnXml } by name, no default export
│   ├── components/
│   │   ├── BpmnCanvas/      # maxGraph canvas host; events, keyboard shortcuts, tool modes
│   │   ├── ContextMenu/     # Right-click menu (delete, morph, add next element)
│   │   ├── Toolbar/         # Floating draggable tool palette
│   │   ├── ElementPanel/    # Wrapper panel shown when an element is selected
│   │   ├── PropertiesPanel/ # Element properties form
│   │   ├── DocumentationPanel/ # Free-text documentation textarea
│   │   ├── ProcessPanel/    # Side panel shown when nothing is selected
│   │   ├── MenuBar/         # Process name, Undo/Redo, "Show XML" toggle
│   │   ├── XmlView/         # Live inline XML editor panel
│   │   ├── ValidationPanel/ # Bottom-drawer BPMN validation panel
│   │   └── ShortcutOverlay/ # Keyboard shortcut reference modal (host-mountable, decoupled from the store)
│   ├── graph/
│   │   ├── graphSetup.js    # initGraph() – maxGraph instance setup
│   │   ├── bpmnStyles.js    # Cell style registration and dark-mode theme switching
│   │   ├── bpmnElements.js  # add*() helpers – insert vertices/edges into graph
│   │   ├── bpmnTypes.js     # BPMN type/subtype definitions + style-name helpers
│   │   ├── bpmnIcons.js     # SVG icon imports (canvas data URIs + toolbar URLs)
│   │   ├── connectionValidation.js  # Connection rule logic (getConnectionRejectionReason)
│   │   ├── xmlExport.js     # exportToBpmn() → BPMN 2.0 XML string; createEmptyBpmnXml()
│   │   └── xmlImport.js     # importFromBpmn() → populates graph from XML
│   ├── store/
│   │   └── modelerStore.js  # Zustand store: graph ref, active tool, theme, readOnly, panel state
│   └── styles/
│       └── tokens.css       # Color/shadow custom properties; imported directly by BpmnModeler.jsx (ships with the package)
├── tests/unit/
│   ├── helpers/fakeGraph.js          # In-memory graph stub (no maxGraph required)
│   ├── bpmnTypes.test.js             # Style-name helpers + getTypeName()
│   ├── connectionValidation.test.js  # Connection rule tests
│   ├── validation.test.js            # runValidation() logic tests
│   ├── xmlRoundtrip.test.js          # XML import/export round-trip tests
│   └── modelerStoreIsolation.test.js # Per-instance store isolation (multiple <BpmnModeler> mounts don't share state)
└── docs/
    ├── bpmn-specification.pdf         # Official BPMN 2.0 spec
    ├── bpmn-examples.pdf              # Companion examples document
    ├── components.md                  # User-facing components/interactions reference
    ├── npm-package-migration.md       # Package-extraction plan/checklist (history)
    ├── npm-package-migration-status.md # What's actually landed vs. still planned
    └── desktop-app-migration-plan.md  # History of Desktop/ becoming a real consumer of this published package instead of a forked copy of src/