---
title: "BPMN"
description: "@tiptap-pro/extension-diagrams/bpmn draws diagrams stored as BPMN 2.0 XML with bpmn-js, and connects the bpmn-js modeler for changing them."
canonical_url: "https://tiptap.dev/docs/diagrams/renderers/bpmn"
---

# BPMN

@tiptap-pro/extension-diagrams/bpmn draws diagrams stored as BPMN 2.0 XML with bpmn-js, and connects the bpmn-js modeler for changing them.

## Quick start

```ts
import { Diagrams } from '@tiptap-pro/extension-diagrams'
import { bpmnDiagramRenderer } from '@tiptap-pro/extension-diagrams/bpmn'
import rendererUrl from '@tiptap-pro/extension-diagrams/bpmn-renderer?url&no-inline'
import viewerUrl from 'bpmn-js/dist/bpmn-viewer.production.min.js?url&no-inline'

Diagrams.configure({
  diagramRenderers: [bpmnDiagramRenderer({ viewerUrl, rendererUrl })],
})

editor.commands.insertFlowDiagram({ source: bpmnXml, format: 'bpmn', title: 'Payment check' })
```

- bpmn-js is not a dependency of the package.
- Install it yourself and serve its bundles as files.
- Its licence is yours to accept.

## Options

| Option        | Type             | Description                                                                                             |
| ------------- | ---------------- | ------------------------------------------------------------------------------------------------------- |
| `viewerUrl`   | `string`         | Required. A bpmn-js bundle: `bpmn-js/dist/bpmn-viewer.production.min.js`. The modeler bundle works too  |
| `rendererUrl` | `string`         | Required. `@tiptap-pro/extension-diagrams/bpmn-renderer`, served as a file. With Vite, `?url&no-inline` |
| `scriptNonce` | `string \| null` | For pages with a nonce-based Content Security Policy                                                    |
| `id`          | `string`         | Default `'bpmn'`                                                                                        |
| `label`       | `string`         | Default `'BPMN'`                                                                                        |

## How it draws

- **bpmn-js** runs in one iframe per page, with `sandbox="allow-scripts"` and a policy that
  allows no network request.
- **The iframe** is laid out off screen, because bpmn-js measures labels while it draws.
- **It loads** the first time a BPMN diagram is drawn.
- **A drawing** is cached by source.
- **Layout:** the XML must carry its layout (BPMN DI), as a modeler saves it. A process
  without one does not draw.
- **Colors:** a diagram is drawn black on white, on a page of its own. The
  `--tt-flow-diagram-*` properties do not reach it.
- **In a PNG, WebP, JPEG or Word export** labels are SVG text, so the file looks like the
  picture in the editor.
- **On a server** a BPMN diagram cannot be exported. bpmn-js lays out text in a browser.

### The bpmn.io attribution

- The Renderer's rules ask for the bpmn.io attribution: a "Powered by bpmn.io" link with the
  bpmn.io logo.
- It is shown below the picture in the editor and drawn into every exported file.
- An integration can add to the rules with `rendererRules`, but not remove the attribution.

## What a BPMN source is

- **The root** is `definitions` in the namespace `http://www.omg.org/spec/BPMN/20100524/MODEL`,
  under any prefix.
- **The file** is closed.
- **It is stored as written**, layout included. Only a byte-order mark and the white space
  around it are removed.
- **Empty source** is allowed, for a process still to be modeled.
- **Pasting BPMN XML as text** gives a BPMN diagram.
- **`detectDiagramInput`** returns `{ kind: 'bpmn' }` for it.

`insertFlowDiagram` and `updateFlowDiagram` return `false` for anything else, and for source
longer than 1,000,000 characters.

## Editing in the modeler

`connectBpmnEditor` runs the bpmn-js modeler in an iframe you created and opens a diagram in
it. You build the dialog, the iframe and the buttons; the package fills the iframe, sandboxes
it with scripts only, and hands back the XML when you ask for it.

```ts
import { connectBpmnEditor } from '@tiptap-pro/extension-diagrams/bpmn'
import rendererUrl from '@tiptap-pro/extension-diagrams/bpmn-renderer?url&no-inline'
import fontUrl from 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css?url&no-inline'
import bpmnStyleUrl from 'bpmn-js/dist/assets/bpmn-js.css?url&no-inline'
import diagramStyleUrl from 'bpmn-js/dist/assets/diagram-js.css?url&no-inline'
import modelerUrl from 'bpmn-js/dist/bpmn-modeler.production.min.js?url&no-inline'

const frame = document.createElement('iframe')
dialog.append(frame)

const connection = connectBpmnEditor(frame, {
  modelerUrl,
  rendererUrl,
  styleUrls: [diagramStyleUrl, bpmnStyleUrl, fontUrl],
  source: diagram.source,
  onReady: () => (saveButton.disabled = false),
})

saveButton.onclick = async () => {
  const source = await connection.save()
  editor.commands.updateFlowDiagram({ pos, source, format: 'bpmn' })
  connection.destroy()
  dialog.close()
}
```

### connectBpmnEditor(frame, options)

| Option             | Type                | Description                                                                                                |
| ------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------- |
| `modelerUrl`       | `string`            | Required. The bpmn-js modeler bundle, `bpmn-js/dist/bpmn-modeler.production.min.js`                        |
| `rendererUrl`      | `string`            | Required. `@tiptap-pro/extension-diagrams/bpmn-renderer`, the same file the Renderer uses                  |
| `styleUrls`        | `readonly string[]` | Required. The modeler's stylesheets: `diagram-js.css`, `bpmn-js.css` and `bpmn-font/css/bpmn-embedded.css` |
| `scriptNonce`      | `string \| null`    | For pages with a nonce-based Content Security Policy                                                       |
| `source`           | `string`            | The BPMN XML to open. Empty opens a new process with a start event                                         |
| `onReady()`        | `() => void`        | The diagram is open and can be edited                                                                      |
| `onChange()`       | `() => void`        | Called after every change in the modeler                                                                   |
| `onError(message)` | `(message) => void` | The modeler did not load, or could not open the source                                                     |

It returns `{ save, destroy }`:

| Function    | Returns                                                                                                          |
| ----------- | ---------------------------------------------------------------------------------------------------------------- |
| `save()`    | A Promise of the diagram as it is now, as BPMN XML. It rejects when the modeler sends something that is not BPMN |
| `destroy()` | Nothing. Stops listening and empties the iframe; the iframe stays yours                                          |

- **Fonts** of the modeler must come from `data:` or from the origin of a stylesheet, so use
  the embedded font stylesheet.
- **The modeler works on a copy:** changes others make to the same diagram while it is open
  are replaced by the next save.
