BPMN Text Annotation in React Diagram Component

21 Oct 202524 minutes to read

Overview

A BPMN object can be associated with a text annotation that provides additional details about objects within a flow without affecting the actual process flow. Text annotations serve as documentation elements that help explain or clarify specific aspects of the BPMN diagram.

A TextAnnotation points to or references another BPMN shape through the textAnnotationTarget property. When the target shape is moved or deleted, any TextAnnotations attached to the shape will automatically move or be deleted as well. This ensures that TextAnnotations remain associated with their target shapes, though the TextAnnotation can be repositioned to any offset from its target.

The annotation element can be switched from one BPMN node to another by simply dragging the source end of the annotation connector to the desired BPMN node. By default, the TextAnnotation shape includes a connection to its target.

  • By default, the TextAnnotation shape has a connection.

The textAnnotationDirection property controls the shape direction of the text annotation. By default, this property is set to Auto, which automatically determines the optimal direction based on the target’s position.

To set the size for text annotation, use the width and height properties of the node.

The offsetX and offsetY properties determine the distance between the BPMN node and the TextAnnotation.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent, Inject, BpmnDiagrams } from "@syncfusion/ej2-react-diagrams";
// A node is created and stored in nodes array.
let nodes = [ 
    {
    id: 'event1', style: { strokeWidth: 2 },
    height:70,width:70,offsetX:400,offsetY:200,
    shape: { type: 'Bpmn', shape: 'Event',
        event: { event: 'Start', trigger: 'None' },
        } 
    },
    //node with target
    {
        id: 'textNode1', width: 70, height: 70,
        offsetX:400,offsetY:400,
        annotations:[{content:'textNode1'}],
            shape: {
                type: 'Bpmn', shape: 'TextAnnotation',
                textAnnotation:{ textAnnotationDirection:'Auto',textAnnotationTarget:'event1'}
            } 
    },
    //Node without target
    {
        id: 'textNode2', width: 70, height: 70,
        offsetX:600,offsetY:400,
        annotations:[{content:'textNode1'}],
            shape: {
                type: 'Bpmn', shape: 'TextAnnotation',
                textAnnotation:{ textAnnotationDirection:'Auto',textAnnotationTarget:''}
            } 
    },]
// initialize diagram component
function App() {
    return (<DiagramComponent id="container" width={'100%'} height={'600px'} 
    // Add node
    nodes={nodes}>
      <Inject services={[BpmnDiagrams]}/>
    </DiagramComponent>);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    Diagram,
    DiagramComponent,
    Inject,
    NodeModel,
    BpmnShape,
    BpmnSubProcessModel,
    BpmnShapeModel,
    BpmnDiagrams,
    BpmnActivityModel,
    BpmnFlowModel,
    BpmnGatewayModel
} from "@syncfusion/ej2-react-diagrams";
// A node is created and stored in nodes array.
let nodes: NodeModel[] = [ 
    {
    id: 'event1', style: { strokeWidth: 2 },
    height:70,width:70,offsetX:400,offsetY:200,
    shape: { 
          type: 'Bpmn', shape: 'Event',
          event: { event: 'Start', trigger: 'None' },
          } 
    },
    //node with target
    {
      id: 'textNode1', width: 70, height: 70,
      offsetX:400,offsetY:400,
      annotations:[{content:'textNode1'}],
          shape: {
              type: 'Bpmn', shape: 'TextAnnotation',
              textAnnotation:{ textAnnotationDirection:'Auto',textAnnotationTarget:'event1'}
          } 
    },
    //Node without target
    {
      id: 'textNode2', width: 70, height: 70,
      offsetX:600,offsetY:400,
      annotations:[{content:'textNode1'}],
          shape: {
              type: 'Bpmn', shape: 'TextAnnotation',
              textAnnotation:{ textAnnotationDirection:'Auto',textAnnotationTarget:''}
          } 
    },]
// initialize diagram component
function App() {
  return (
    <DiagramComponent
      id="container"
      width={'100%'}
      height={'600px'}
      // Add node
      nodes={nodes}
    >
      <Inject services={[BpmnDiagrams]} />
    </DiagramComponent>
  );
}

const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);

Text Annotation in Palette.

Text annotation nodes can be rendered in the symbol palette alongside other BPMN shapes. The following example demonstrates how to render BPMN text annotation nodes in the symbol palette.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { SymbolPaletteComponent,Diagram,BpmnDiagrams, DiagramComponent} from "@syncfusion/ej2-react-diagrams";

Diagram.Inject(BpmnDiagrams);
SymbolPaletteComponent.Inject(BpmnDiagrams);
//Initialize the basicshapes for the symbol palette
export function getBPMNShapes() {
    let bpmnShapes = [
        {
            id: 'Start',
            width: 35,
            height: 35,
            shape: {
              type: 'Bpmn',
              shape: 'Event',
              event: { event: 'Start' },
            },
          },
          {
            id: 'Gateway',
            width: 35,
            height: 35,
            offsetX: 100,
            offsetY: 100,
            shape: {
              type: 'Bpmn',
              shape: 'Gateway',
              gateway: { type: 'Exclusive' }
            },
          },
          {
            id: 'DataObject',
            width: 35,
            height: 35,
            offsetX: 500,
            offsetY: 100,
            shape: {
              type: 'Bpmn',
              shape: 'DataObject',
              dataObject: { collection: false, type: 'None' },
            },
          },
          {
            id: 'textAnnotation',
            width: 35,
            height: 35,
            shape: {
              type: 'Bpmn',
              shape: 'TextAnnotation',
            },
            annotations: [{ content: 'textAnnotation' }],
          },
    ];
    return bpmnShapes;
}
//Initializes the symbol palette
function App() {
    return (
        <div style={{ width: '100%' }}>
            <div id="palette-space" className="sb-mobile-palette">
            <SymbolPaletteComponent
                id="container"
                palettes={[
                {
                    id: 'uml',
                    expanded: true,
                    symbols: getBPMNShapes(),
                    title: 'BPMN Shapes',
                },
                ]}
                symbolHeight={80}
                symbolWidth={80}
                getNodeDefaults={(symbol) => {
                symbol.width = 100;
                symbol.height = 100;
                }}
                //Sets the margin of the dragging helper relative to the mouse cursor
                symbolMargin={{
                left: 12,
                right: 12,
                top: 12,
                bottom: 12,
                }}
                getSymbolInfo={(symbol) => {
                //Defines the symbol description
                return { fit: true, description: { text: symbol.id } };
                }}
            />
            </div>
            <div id="diagram-space" className="sb-mobile-diagram">
            <DiagramComponent
                id="diagram"
                width={'800px'}
                height={'445px'}
            ></DiagramComponent>
            </div>
        </div>
    );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from 'react-dom';
import { SymbolPaletteComponent,Diagram,BpmnDiagrams, DiagramComponent} from "@syncfusion/ej2-react-diagrams";

Diagram.Inject(BpmnDiagrams);
SymbolPaletteComponent.Inject(BpmnDiagrams);
//Initialize the basicshapes for the symbol palette
export function getBPMNShapes() {
    let bpmnShapes = [
        {
            id: 'Start',
            width: 35,
            height: 35,
            shape: {
              type: 'Bpmn',
              shape: 'Event',
              event: { event: 'Start' },
            },
          },
          {
            id: 'Gateway',
            width: 35,
            height: 35,
            offsetX: 100,
            offsetY: 100,
            shape: {
              type: 'Bpmn',
              shape: 'Gateway',
              gateway: { type: 'Exclusive' }
            },
          },
          {
            id: 'DataObject',
            width: 35,
            height: 35,
            offsetX: 500,
            offsetY: 100,
            shape: {
              type: 'Bpmn',
              shape: 'DataObject',
              dataObject: { collection: false, type: 'None' },
            },
          },
          {
            id: 'textAnnotation',
            width: 35,
            height: 35,
            shape: {
              type: 'Bpmn',
              shape: 'TextAnnotation',
            },
            annotations: [{ content: 'textAnnotation' }],
          },
    ];
    return bpmnShapes;
}
//Initializes the symbol palette
function App() {
    return (
        <div style={{ width: '100%' }}>
            <div id="palette-space" className="sb-mobile-palette">
            <SymbolPaletteComponent
                id="container"
                palettes={[
                {
                    id: 'uml',
                    expanded: true,
                    symbols: getBPMNShapes(),
                    title: 'BPMN Shapes',
                },
                ]}
                symbolHeight={80}
                symbolWidth={80}
                getNodeDefaults={(symbol) => {
                symbol.width = 100;
                symbol.height = 100;
                }}
                //Sets the margin of the dragging helper relative to the mouse cursor
                symbolMargin={{
                left: 12,
                right: 12,
                top: 12,
                bottom: 12,
                }}
                getSymbolInfo={(symbol) => {
                //Defines the symbol description
                return { fit: true, description: { text: symbol.id } };
                }}
            />
            </div>
            <div id="diagram-space" className="sb-mobile-diagram">
            <DiagramComponent
                id="diagram"
                width={'800px'}
                height={'445px'}
            ></DiagramComponent>
            </div>
        </div>
    );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);

Connect the TextAnnotation to BPMN Node

Users can drag and drop any BPMN shapes from the palette to the diagram and establish connections between BPMN nodes and text annotations through interactive manipulation.

The following image demonstrates how to drag a symbol from the palette and connect the text annotation to a BPMN node using interaction.

Text annotation GIF

Text Annotation Direction

The text annotation supports several directional orientations to optimize the visual layout of the diagram:
| Text annotation direction | Image |
| ——– | ——– |
| Auto | BPMN text annotation direction auto |
| Left | BPMN text annotation direction left |
| Right | BPMN text annotation direction right |
| Top | BPMN text annotation direction top |
| Bottom | BPMN text annotation direction bottom |

Add Text Annotation at Runtime

Text annotations can be added dynamically using either the addTextAnnotation method or the add method of the diagram. The following example shows how to use these methods to add a text annotation node programmatically.

import * as React from "react";
import * as ReactDOM from 'react-dom';
import { DiagramComponent, Inject, BpmnDiagrams, randomId } from "@syncfusion/ej2-react-diagrams";
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';

let diagramInstance;
let nodes = [
    {
        id: 'event',
        offsetX: 200,
        offsetY: 200,
        width: 70,
        height: 70,
        shape: { type: 'Bpmn', shape: 'Event' },
    },
]
const addTextAnnotation = function () {
    let event = diagramInstance.nodes[0];
    let textAnnotation = {
        name: 'newAnnotation' + randomId(),
        angle: 0,
        length: 100,
        width: 100,
        height: 40,
        text: 'New Annotation',
    };
    /**
     * parameter 1 - TextAnnotation to be added
     * parameter 2 - The parent node where the text annotation will be added as a child.
     */
    diagramInstance.addTextAnnotation(textAnnotation, event);
}

const addTextAnnotationNode = function () {
    let textAnnotation = {
        id: 'textAnnotation' + randomId(),
        offsetX: 300,
        offsetY: 100,
        width: 100,
        height: 40,
        annotations: [{ content: 'Text Annotation' }],
        shape: {
            type: 'Bpmn',
            shape: 'TextAnnotation',
            textAnnotation: {
                //Parent node of text annotation
                textAnnotationTarget: 'event',
                textAnnotationDirection: 'Auto',
            },
        },
    };
    /**
     * parameter 1 - TextAnnotation to be added to the event node
     */
    diagramInstance.add(textAnnotation);
}

const addTextAnnotationAlone = function () {
    let textAnnotation = {
        id: 'textAnnotationAlone' + randomId(),
        offsetX: 300,
        offsetY: 300,
        width: 100,
        height: 70,
        annotations: [{ content: 'Text Annotation' }],
        shape: {
            type: 'Bpmn',
            shape: 'TextAnnotation',
        },
    };
    /**
     * parameter 1 - TextAnnotation to be added to diagram without parent
     */
    diagramInstance.add(textAnnotation);
}
// initialize diagram component 
function App() {
    return (<div>
        <ButtonComponent content="Add text Annotation" onClick={addTextAnnotation} />
        <ButtonComponent content="Add text Annotation Node" onClick={addTextAnnotationNode} />
        <ButtonComponent content="Add text Annotation Alone" onClick={addTextAnnotationAlone} /><DiagramComponent id="container" width={'100%'} height={'600px'} ref={(diagram) => (diagramInstance = diagram)}
            // Add node
            nodes={nodes}
        >
            <Inject services={[BpmnDiagrams]} />
        </DiagramComponent></div>);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from 'react-dom';
import { DiagramComponent, NodeModel, Node, Inject, BpmnDiagrams, randomId } from "@syncfusion/ej2-react-diagrams";
import { ButtonComponent } from '@syncfusion/ej2-react-buttons';

let diagramInstance:DiagramComponent;
let nodes: NodeModel[] = [
  {
    id: 'event',
    offsetX: 200,
    offsetY: 200,
    width: 70,
    height: 70,
    shape: { type: 'Bpmn', shape: 'Event' },
  },
]
const addTextAnnotation = function () {
  let event = diagramInstance.nodes[0];
  let textAnnotation = {
    name: 'newAnnotation' + randomId(),
    angle: 0,
    length: 100,
    width: 100,
    height: 40,
    text: 'New Annotation',
  };
  /**
   * parameter 1 - TextAnnotation to be added
   * parameter 2 - The parent node where the text annotation will be added as a child.
   */
  diagramInstance.addTextAnnotation(textAnnotation, event);
}

const addTextAnnotationNode = function () {
  let textAnnotation = {
    id: 'textAnnotation' + randomId(),
    offsetX: 300,
    offsetY: 100,
    width: 100,
    height: 40,
    annotations: [{ content: 'Text Annotation' }],
    shape: {
      type: 'Bpmn',
      shape: 'TextAnnotation',
      textAnnotation: {
        //Parent node of text annotation
        textAnnotationTarget: 'event',
        textAnnotationDirection: 'Auto',
      },
    },
  };
  /**
   * parameter 1 - TextAnnotation to be added to the event node
   */
  diagramInstance.add(textAnnotation as Node);
}

const addTextAnnotationAlone = function () {
  let textAnnotation = {
    id: 'textAnnotationAlone' + randomId(),
    offsetX: 300,
    offsetY: 300,
    width: 100,
    height: 70,
    annotations: [{ content: 'Text Annotation' }],
    shape: {
      type: 'Bpmn',
      shape: 'TextAnnotation',
    },
  };
  /**
   * parameter 1 - TextAnnotation to be added to diagram without parent
   */
  diagramInstance.add(textAnnotation as Node);
}
// initialize diagram component 
function App() {
  return (<div>
    <ButtonComponent content="Add text Annotation" onClick={addTextAnnotation} />
    <ButtonComponent content="Add text Annotation Node" onClick={addTextAnnotationNode} />
    <ButtonComponent content="Add text Annotation Alone" onClick={addTextAnnotationAlone} /><DiagramComponent id="container" width={'100%'} height={'600px'} ref={(diagram) => (diagramInstance = diagram)}
      // Add node
      nodes={nodes}
    >
      <Inject services={[BpmnDiagrams]} />
    </DiagramComponent></div>);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);