
Syncfusion React Diagram
- 449 installs
- 3 repo stars
- Updated July 28, 2026
- syncfusion/react-ui-components-skills
syncfusion-react-diagram is an agent skill that guides accurate Syncfusion React Diagram implementation for developers who build flowcharts, org charts, mind maps, and interactive node-and-connector UIs.
About
syncfusion-react-diagram is a component-specific agent skill from syncfusion/react-ui-components-skills that teaches coding agents the official Syncfusion React Diagram APIs. Each skill ships as a self-contained SKILL.md playbook plus references/ implementation guides covering setup, imports, modules, shapes, connectors, data binding, and automatic layouts. Developers install the pack with npx skills add syncfusion/react-ui-components-skills so agents generate production-ready diagram code grounded in Syncfusion patterns instead of hallucinated props. The Diagram component supports flowchart diagrams with built-in shapes, organization charts, mind maps, radial tree layouts, and hierarchical tree charts with flexible data binding. Reach for syncfusion-react-diagram when building admin builders, process designers, or architecture canvases in React apps already using Syncfusion. The skill pairs with sibling Syncfusion React skills and documents theme styling, globalization, and feature integration conventions shared across the pack.
- syncfusion-react-diagram
Syncfusion React Diagram by the numbers
- 449 all-time installs (skills.sh)
- +52 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #966 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/react-ui-components-skills --skill syncfusion-react-diagramAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 449 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/react-ui-components-skills ↗ |
How do you implement Syncfusion React Diagram components?
Use syncfusion-react-diagram for development tasks
Who is it for?
React developers using Syncfusion who need agent-generated diagram, flowchart, or org-chart UIs with correct API usage.
Skip if: Teams using D3, React Flow, or Mermaid-only diagrams without Syncfusion licenses or components.
When should I use this skill?
A user asks to build a React flowchart, org chart, mind map, or node-connector editor with Syncfusion Diagram.
What you get
React Diagram components with nodes, connectors, automatic layouts, and Syncfusion module imports ready to run.
- React Diagram component code
- Connector and layout configuration
Files
Syncfusion React Diagram
The Syncfusion React Diagram component enables building rich, interactive diagrams including flowcharts, organizational charts, BPMN process flows, UML diagrams, mind maps, and network diagrams. It renders using SVG and supports extensive customization through nodes, connectors, ports, annotations, layouts, and data binding.
Table of Contents
- When to Use This Skill
- Component Overview
- Documentation and Navigation Guide
- Getting Started
- Nodes
- Connectors
- Labels and Annotations
- Ports
- Shapes and Styles
- BPMN Diagrams
- UML Diagrams
- Layouts
- Swimlanes
- Groups and Containers
- Symbol Palette
- Data Binding
- Interaction and Tools
- Serialization and Export
- Diagram Settings
- Quick Start Example
- Common Patterns
- Module Injection Reference
- Key Props
When to Use This Skill
Use this skill when users need to:
- Build flowcharts representing processes, decision trees, or workflows
- Create org charts from hierarchical or flat data sources
- Model BPMN diagrams with activities, events, gateways, and flows
- Draw UML diagrams including class diagrams and sequence diagrams
- Visualize mind maps or radial layouts from data
- Design network/entity diagrams with nodes and relationship connectors
- Implement swimlane diagrams with lanes and phases
- Drag and drop shapes from a symbol palette onto a canvas
- Bind data to automatically generate diagram structure
- Serialize and restore diagram state as JSON
- Export or print diagram content as image/SVG
Important: API Verification Required
API Verification Required: Always verify API class names, properties, and signatures by reading reference files (references/*.md) BEFORE generating code examples. Do not assume or infer class names.
Component Overview
The Diagram component is built around:
- Nodes — Graphical shapes (rectangles, circles, flow shapes, custom) placed on the canvas
- Connectors — Lines/arrows linking nodes or freestanding points
- Annotations — Text labels attached to nodes or connectors
- Ports — Named connection points on nodes for precise connector anchoring
- Layouts — Automatic arrangement algorithms (hierarchical, org-chart, mindmap, radial, flowchart)
- Symbol Palette — Sidebar of draggable shape symbols
- Module System — Opt-in feature modules (
Injectdirective) to keep bundle size lean
All diagram elements are rendered using SVG for crisp, resolution-independent graphics.
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
When to read:
- First-time setup of the Diagram component
- Installing packages, CSS imports, and theme configuration
- Creating a minimal
DiagramComponent - Understanding the
Injectmodule system - Next.js or Preact integration
Covers:
- npm install and dependencies
- Vite/CRA project setup
- CSS theme import paths
- Basic
DiagramComponentwithnodesandconnectors - Module injection with
<Inject services={[...]} /> - Next.js and Preact usage notes
---
Nodes
📄 Read: references/nodes.md
When to read:
- Adding, positioning, and sizing nodes
- Customizing node appearance (fill, stroke, shadow)
- Working with node shapes (Flow, Basic, Path, Image, HTML)
- Handling node events (click, drag, resize)
- Expanding/collapsing nodes at runtime
- Setting default node properties via
getNodeDefaults
Covers:
- Creating nodes via
nodescollection offsetX/offsetY,width/heightpositioning- Adding/removing nodes at runtime (
diagram.add,diagram.remove) - Style properties (fill, strokeColor, opacity, gradient)
getNodeDefaultspattern for consistent styling- Node expand/collapse with
expandIcon/collapseIcon - Node interaction events
---
Connectors
📄 Read: references/connectors.md
When to read:
- Drawing lines/arrows between nodes or freestanding points
- Choosing connector type (Straight, Orthogonal, Bezier)
- Configuring connector segments and routing
- Customizing decorator (arrow) shapes
- Setting
sourceID/targetIDto link nodes - Setting
getConnectorDefaultsfor consistent styling
Covers:
- Connector types:
Straight,Orthogonal,Bezier sourcePoint/targetPointfor freestanding connectorssourceID/targetIDfor node-to-node connections- Multiple segment configuration
- Bezier control points and segment edit orientation
- Arrow/decorator customization
- Connector events and interaction
getConnectorDefaultspattern
---
Labels and Annotations
📄 Read: references/labels-and-annotations.md
When to read:
- Adding text labels to nodes or connectors
- Styling annotation text (font, color, bold, alignment)
- Making labels draggable or editable
- Positioning labels relative to nodes/connectors
- Handling label-specific events
Covers:
annotationsarray on nodes and connectors- Label
content,style,offset,alignment - Node labels vs. connector labels
- Label interaction (edit on double-click, drag)
- Label appearance (background, border, padding)
- Label events
---
Ports
📄 Read: references/ports.md
When to read:
- Creating named connection points on nodes
- Controlling where connectors attach to a node
- Styling and positioning ports
- Restricting connector connections to specific ports
- Handling port interaction events
Covers:
- Port types and
portsarray on nodes - Port positioning (
offset,alignment) - Port appearance (shape, fill, stroke)
- Connecting connectors to specific ports via
sourcePortID/targetPortID - Port interaction and visibility options
- Port events
---
Shapes and Styles
📄 Read: references/shapes-and-styles.md
When to read:
- Choosing the right shape type (Flow, Basic, Path, Image, HTML, Native SVG)
- Applying fill colors, gradients, and stroke styles
- Customizing node appearance with CSS or themes
- Understanding shape property structure
Covers:
shape.typeoptions:Flow,Basic,Path,Image,HTML,Native- Flowchart shape values (Terminator, Process, Decision, etc.)
- Basic shape values (Rectangle, Ellipse, Diamond, etc.)
- Style properties:
fill,strokeColor,strokeWidth,strokeDashArray,opacity - Linear and radial gradients
- Shadow effect
- CSS class-based styling
---
BPMN Diagrams
📄 Read: references/bpmn-diagrams.md
When to read:
- Modeling business process flows using BPMN notation
- Using BPMN-specific shapes (activities, events, gateways)
- Drawing sequence flows, message flows, associations
- Adding data objects, data sources, text annotations, and groups
Covers:
BpmnDiagramsmodule injection- BPMN shape type:
shape.type = 'Bpmn' - Activities: Task types, Subprocess, Expanded Subprocess
- Events: Start, End, Intermediate events and their triggers
- Gateways: Exclusive, Parallel, Inclusive, Complex
- Flows: Sequence, Message, Association
- Data objects and data sources
- BPMN groups and text annotations
---
UML Diagrams
📄 Read: references/uml-diagrams.md
When to read:
- Building UML class diagrams with attributes and methods
- Modeling relationships (association, generalization, dependency)
- Creating UML sequence diagrams with lifelines and messages
- Working with classifiers (class, interface, enumeration, collaboration)
Covers:
- UML class diagram shapes (
shape.type = 'UmlClassifier') - Classifier types: Class, Interface, Enumeration, Collaboration
- Attributes, operations, and members syntax
- UML relationship connector types
- UML sequence diagram (
shape.type = 'UmlActivity') - Lifelines, messages, and fragments
---
Layouts
📄 Read: references/layouts.md
When to read:
- Automatically arranging nodes in a tree, hierarchy, or radial pattern
- Creating org charts driven by parent-child data
- Building mind maps from data sources
- Configuring flowchart layouts with decision/process shapes
- Customizing layout spacing, orientation, and alignment
Covers:
HierarchicalTree— top-down/left-right tree layoutsOrganizationalChart— classic org chart withgetNodeDefaults/getConnectorDefaultsMindMap— branching mind map layoutRadialTree— circular radial layoutFlowchartlayout (automatic flow arrangement)ComplexHierarchicalTreefor multi-parent hierarchieslayoutproperty:type,orientation,margin,horizontalSpacing,verticalSpacingLayoutAnimationmodule- Layout events
---
Swimlanes
📄 Read: references/swimlanes.md
When to read:
- Creating multi-lane process diagrams
- Adding phases to represent timeline or stages
- Configuring swimlane headers and orientation
- Placing child nodes inside lanes
- Using swimlane shapes from the symbol palette
Covers:
- Swimlane
shape.type = 'SwimLane' - Lanes and phases configuration
- Header text, size, and orientation
- Child nodes inside lanes
- Swimlane palette integration
- Swimlane interaction (add/remove lanes)
---
Groups and Containers
📄 Read: references/groups-and-containers.md
When to read:
- Grouping multiple nodes for collective movement/selection
- Adding/removing children from groups at runtime
- Using container nodes to visually bound child nodes
- Configuring padding, fit-to-children behavior
Covers:
- Group node
shape.type = 'Group'andchildrenarray - Grouping/ungrouping nodes via diagram methods
- Adding children at runtime
- Container node configuration
- Padding and auto-fit behavior for containers
---
Symbol Palette
📄 Read: references/symbol-palette.md
When to read:
- Adding a drag-and-drop shape sidebar to your diagram app
- Defining custom symbol sets (palettes)
- Customizing palette icon size, search behavior, and accordion
- Handling
symbolDragEnter/symbolDropevents
Covers:
SymbolPaletteComponentsetup alongsideDiagramComponent- Defining
paletteswith symbol arrays getSymbolInfofor display customization- Palette accordion: expanding/collapsing groups
- Search functionality
- Symbol preview size configuration
- Palette events
---
Data Binding
📄 Read: references/data-binding.md
When to read:
- Generating diagrams automatically from JSON or remote data
- Mapping
idandparentIdfields for hierarchy - Using
DataManagerfor remote or filtered data - Customizing node appearance based on data properties (
setNodeTemplate) - Connecting to a PostgreSQL or external data source
Covers:
dataSourceSettingspropertyid,parentIdfield mappingDataManagerwith local arrays or remote URLsgetNodeDefaultsandsetNodeTemplatefor data-driven styling- PostgreSQL data source integration
- Rendering org charts from flat JSON data
---
Interaction and Tools
📄 Read: references/interaction-and-tools.md
When to read:
- Enabling/disabling node and connector interactions (drag, resize, select)
- Switching diagram tool modes (pointer, draw, pan)
- Configuring diagram constraints
- Adding custom keyboard commands or toolbar actions
- Implementing undo/redo
- Setting up context menus or user handles
Covers:
DiagramConstraints,NodeConstraints,ConnectorConstraintstoolproperty:DiagramTools.Default,ZoomPan,DrawOnce- Selection, drag, resize, rotate interactions
commandsfor keyboard shortcuts- Undo/Redo (
UndoRedomodule) - Context menu (
DiagramContextMenumodule) - User handles for custom action buttons on nodes
Snappingmodule and snap settings
---
Serialization and Export
📄 Read: references/serialization-and-export.md
When to read:
- Saving and restoring diagram state as JSON
- Exporting the diagram as PNG, JPEG, or SVG
- Printing diagram content
- Importing Visio (.vsdx) files
- Migrating from EJ1 diagram format
Covers:
diagram.saveDiagram()anddiagram.loadDiagram(json)PrintAndExportmodule injection- Export settings: format, region, margin, background
- Print dialog and options
- Visio file import (
visio.md) - EJ1 serialization compatibility (
Ej1Serializationmodule)
---
Diagram Settings
📄 Read: references/diagram-settings.md
When to read:
- Managing diagram layers (visibility, locking, ordering)
- Enabling virtualization for performance with large diagrams
- Configuring grid lines, snapping, and rulers
- Setting page size, orientation, and margins
- Configuring scroll behavior and limits
- Adding tooltips to diagram elements
- Using the Overview component for minimap navigation
- Enabling localization or RTL support
- Understanding accessibility features
Covers:
- Layers: add, lock, set visibility, active layer
Virtualizationmodule for large diagrams- Grid lines: dots, lines, snap settings
- Ruler configuration
scrollSettings:scrollLimit,minZoom,maxZoompageSettings: size, orientation, background, multiplePage- Tooltip on nodes and connectors
OverviewComponentminimap setup- Localization and RTL
- ARIA and keyboard accessibility
---
Quick Start Example
Minimal Flowchart
import { DiagramComponent, NodeModel, ConnectorModel, Inject, UndoRedo } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [
{ id: 'start', offsetX: 300, offsetY: 80, width: 140, height: 50,
shape: { type: 'Flow', shape: 'Terminator' }, annotations: [{ content: 'Start' }] },
{ id: 'process', offsetX: 300, offsetY: 180, width: 140, height: 50,
shape: { type: 'Flow', shape: 'Process' }, annotations: [{ content: 'Process' }] },
{ id: 'end', offsetX: 300, offsetY: 290, width: 140, height: 50,
shape: { type: 'Flow', shape: 'Terminator' }, annotations: [{ content: 'End' }] },
];
const connectors: ConnectorModel[] = [
{ id: 'c1', sourceID: 'start', targetID: 'process' },
{ id: 'c2', sourceID: 'process', targetID: 'end' },
];
export default function App() {
return (
<DiagramComponent id="diagram" width={'100%'} height={'500px'}
nodes={nodes} connectors={connectors}
getConnectorDefaults={(obj) => { obj.type = 'Orthogonal'; return obj; }}>
<Inject services={[UndoRedo]} />
</DiagramComponent>
);
}CSS import: @import '@syncfusion/ej2-react-diagrams/styles/material.css'; and peer CSS for ej2-base, ej2-popups, ej2-splitbuttons, ej2-navigations.
---
Common Patterns
Pattern 1: Org Chart from Data Source
import { DiagramComponent, HierarchicalTree, DataBinding, Inject } from '@syncfusion/ej2-react-diagrams';
import { DataManager } from '@syncfusion/ej2-data';
const data = [
{ Name: 'CEO' },
{ Name: 'CTO', ReportingPerson: 'CEO' },
{ Name: 'Dev Lead', ReportingPerson: 'CTO' },
];
export default function App() {
return (
<DiagramComponent id="diagram" width={'100%'} height={'500px'}
dataSourceSettings={{ id: 'Name', parentId: 'ReportingPerson', dataManager: new DataManager(data) }}
layout={{ type: 'OrganizationalChart' }}
getNodeDefaults={(node) => { node.width = 120; node.height = 40; return node; }}
getConnectorDefaults={(obj) => { obj.type = 'Orthogonal'; return obj; }}>
<Inject services={[DataBinding, HierarchicalTree]} />
</DiagramComponent>
);
}Pattern 2: Save and Load Diagram State
Use diagramInstance.saveDiagram() to serialize to JSON string; diagramInstance.loadDiagram(json) to restore. Inject UndoRedo for history support. For full examples see references/serialization-and-export.md.
Pattern 3: Diagram with Symbol Palette
Render SymbolPaletteComponent alongside DiagramComponent. Define palettes with shape arrays, set symbolHeight/symbolWidth, and users can drag shapes onto the canvas. For full examples see references/symbol-palette.md.
---
Module Injection Reference
Inject only the modules your diagram uses: <Inject services={[HierarchicalTree, DataBinding, UndoRedo]} />
| Module | Purpose |
|---|---|
HierarchicalTree | Tree and org chart layouts |
MindMap | Mind map layout |
RadialTree | Radial/circular layout |
ComplexHierarchicalTree | Multi-parent hierarchies |
DataBinding | Data source integration |
UndoRedo | Undo/redo history |
Snapping | Grid snapping |
PrintAndExport | Export & print |
BpmnDiagrams | BPMN shapes |
LayoutAnimation | Animated layouts |
DiagramContextMenu | Right-click context menu |
ConnectorBridging | Bridge overlapping connectors |
ConnectorEditing | Edit connector segments |
Ej1Serialization | Load EJ1 diagram JSON |
---
Key Props
| Prop | Type | Description |
|---|---|---|
nodes | NodeModel[] | Array of node definitions |
connectors | ConnectorModel[] | Array of connector definitions |
layout | LayoutModel | Automatic layout configuration |
dataSourceSettings | DataSourceModel | Data source for auto-generated diagrams |
getNodeDefaults | (node) => NodeModel | Default properties for all nodes |
getConnectorDefaults | (obj) => ConnectorModel | Default properties for all connectors |
setNodeTemplate | (node) => DiagramElement | Custom template rendering for nodes |
tool | DiagramTools | Active interaction tool mode |
snapSettings | SnapSettingsModel | Grid and snap configuration |
scrollSettings | ScrollSettingsModel | Scroll limits and zoom bounds |
pageSettings | PageSettingsModel | Page size, orientation, background |
constraints | DiagramConstraints | Enable/disable diagram-level interactions |
width | string | Diagram canvas width (e.g., '100%') |
height | string | Diagram canvas height (e.g., '600px') |
Related Components
- Chart - Data-driven charts and graphs
- Kanban - Kanban board visualization
- Data Grid - Tabular data display
BPMN Diagrams in Syncfusion React Diagram
Table of Contents
- Overview
- Module Setup
- Events
- Activities (Tasks and Subprocesses)
- Gateways
- Flows (Connectors)
- Data Objects and Data Sources
- BPMN Groups
- Text Annotations
- Complete Example
- Shape Reference
- Troubleshooting
---
Overview
BPMN (Business Process Model and Notation) is the standard for modeling business processes visually. The diagram uses standardized shapes to represent events, activities, gateways, and data flow so that any stakeholder can read and understand the process.
Inject the BpmnDiagrams module to enable BPMN shapes.
---
Module Setup
import { BpmnDiagrams, DiagramComponent, Inject, NodeModel } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [
{
id: 'startEvent',
offsetX: 100, offsetY: 200,
width: 60, height: 60,
shape: {
type: 'Bpmn',
shape: 'Event',
event: { event: 'Start', trigger: 'None' }
}
},
];
export default function App() {
return (
<DiagramComponent id="bpmn" width={'100%'} height={'600px'} nodes={nodes}>
<Inject services={[BpmnDiagrams]} />
</DiagramComponent>
);
}---
Events
Events are circles that mark the start, middle, or end of a process. Set shape.shape: 'Event' and configure event.event and event.trigger.
Event types
event.event | Description |
|---|---|
'Start' | Process begins here |
'Intermediate' | Catching event in the middle of a flow |
'NonInterruptingStart' | Non-interrupting start (dashed border) |
'NonInterruptingIntermediate' | Non-interrupting intermediate |
'ThrowingIntermediate' | Throwing event |
'End' | Process terminates here |
Event trigger types
'None' | 'Message' | 'Timer' | 'Error' | 'Escalation' | 'Signal' | 'Link' | 'Cancel' | 'Compensation' | 'Terminate' | 'Conditional' | 'Multiple' | 'ParallelMultiple'
const nodes: NodeModel[] = [
{
id: 'startEvent',
offsetX: 100, offsetY: 200,
width: 60, height: 60,
shape: {
type: 'Bpmn',
shape: 'Event',
event: { event: 'Start', trigger: 'None' }
}
},
{
id: 'timerEvent',
offsetX: 250, offsetY: 200,
width: 60, height: 60,
shape: {
type: 'Bpmn',
shape: 'Event',
event: { event: 'Intermediate', trigger: 'Timer' }
}
},
{
id: 'endEvent',
offsetX: 400, offsetY: 200,
width: 60, height: 60,
shape: {
type: 'Bpmn',
shape: 'Event',
event: { event: 'End', trigger: 'None' }
}
}
];---
Activities (Tasks and Subprocesses)
Activities represent work. Set shape.shape: 'Activity' and configure activity.activity.
Task
const nodes: NodeModel[] = [
{
id: 'userTask',
offsetX: 250, offsetY: 200,
width: 120, height: 60,
annotations: [{ content: 'Approve Request' }],
shape: {
type: 'Bpmn',
shape: 'Activity',
activity: {
activity: 'Task',
task: {
type: 'User', // 'None' | 'User' | 'Service' | 'Manual' | 'BusinessRule' | 'Send' | 'Receive' | 'Script'
loop: 'None', // 'None' | 'Standard' | 'ParallelMultiInstance' | 'SequenceMultiInstance'
}
}
}
}
];Subprocess
const nodes: NodeModel[] = [
{
id: 'sub1',
offsetX: 300, offsetY: 300,
width: 200, height: 120,
shape: {
type: 'Bpmn',
shape: 'Activity',
activity: {
activity: 'SubProcess',
subProcess: {
collapsed: true, // show as collapsed (box with + marker)
type: 'Transaction', // 'None' | 'Transaction' | 'EventSubProcess'
loop: 'None'
}
}
}
}
];---
Gateways
Gateways are diamond shapes that represent decision points. Set shape.shape: 'Gateway' and configure gateway.type.
gateway.type | Symbol | Description |
|---|---|---|
'None' | Empty diamond | Unspecified gateway |
'Exclusive' | X | Only one path proceeds |
'Inclusive' | Circle | One or more paths proceed |
'Parallel' | + | All paths proceed simultaneously |
'Complex' | * | Complex conditions |
'EventBased' | Circle+pentagon | Next event determines path |
'ExclusiveEventBased' | Variant | Event-based exclusive |
'ParallelEventBased' | + variant | Event-based parallel |
const nodes: NodeModel[] = [
{
id: 'gateway1',
offsetX: 350, offsetY: 200,
width: 60, height: 60,
annotations: [{ content: 'Approved?', offset: { x: 0.5, y: 1.3 } }],
shape: {
type: 'Bpmn',
shape: 'Gateway',
gateway: { type: 'Exclusive' }
}
}
];---
Flows (Connectors)
BPMN uses three types of flows between elements, all set via connector shape.type: 'Bpmn':
Sequence flow (execution order)
const connectors: ConnectorModel[] = [
{
id: 'seq1',
sourceID: 'startEvent', targetID: 'userTask',
type: 'Orthogonal',
shape: {
type: 'Bpmn',
flow: 'Sequence',
sequence: 'Normal' // 'Normal' | 'Default' | 'Conditional'
}
}
];Message flow (communication between pools)
const connectors: ConnectorModel[] = [
{
id: 'msg1',
sourcePoint: { x: 100, y: 100 }, targetPoint: { x: 300, y: 200 },
type: 'Orthogonal',
shape: {
type: 'Bpmn',
flow: 'Message',
message: 'InitiatingMessage' // 'Default' | 'InitiatingMessage' | 'NonInitiatingMessage'
}
}
];Association flow (links to text annotations)
const connectors: ConnectorModel[] = [
{
id: 'assoc1',
sourcePoint: { x: 200, y: 100 }, targetPoint: { x: 300, y: 200 },
type: 'Orthogonal',
shape: {
type: 'Bpmn',
flow: 'Association',
association: 'Directional' // 'Default' | 'Directional' | 'BiDirectional'
}
}
];---
Data Objects and Data Sources
Data object
const nodes: NodeModel[] = [
{
id: 'data1',
offsetX: 400, offsetY: 150,
width: 50, height: 70,
annotations: [{ content: 'Order Form', offset: { x: 0.5, y: 1.3 } }],
shape: {
type: 'Bpmn',
shape: 'DataObject',
dataObject: {
type: 'None', // 'None' | 'Input' | 'Output'
collection: false
}
}
}
];Data source (database)
const nodes: NodeModel[] = [
{
id: 'db1',
offsetX: 500, offsetY: 300,
width: 70, height: 70,
annotations: [{ content: 'Customer DB', offset: { x: 0.5, y: 1.3 } }],
shape: { type: 'Bpmn', shape: 'DataSource' }
}
];---
BPMN Groups
BPMN groups visually bracket related elements without affecting the flow logic.
const nodes: NodeModel[] = [
{
id: 'group1',
offsetX: 300, offsetY: 200,
width: 250, height: 150,
shape: { type: 'Bpmn', shape: 'Group' }
}
];---
Text Annotations
Text annotations add explanatory notes attached to flow elements via association connectors.
const nodes: NodeModel[] = [
{
id: 'note1',
offsetX: 500, offsetY: 100,
width: 120, height: 50,
shape: {
type: 'Bpmn',
shape: 'TextAnnotation',
textAnnotation:{ textAnnotationDirection:'Auto',textAnnotationTarget:''}
}
}
];
// Connect with association flow
const connectors: ConnectorModel[] = [
{
id: 'a1',
sourceID: 'userTask', targetID: 'note1',
shape: { type: 'Bpmn', flow: 'Association', association: 'Directional' }
}
];---
Complete Example
A minimal approval workflow:
import { Diagram, DiagramComponent, Inject, NodeModel, ConnectorModel, BpmnDiagrams } from '@syncfusion/ej2-react-diagrams';
Diagram.Inject(BpmnDiagrams);
const nodes: NodeModel[] = [
{ id: 'start', offsetX: 80, offsetY: 200, width: 50, height: 50,
shape: { type: 'Bpmn', shape: 'Event', event: { event: 'Start', trigger: 'None' } } },
{ id: 'review', offsetX: 250, offsetY: 200, width: 120, height: 60,
annotations: [{ content: 'Review' }],
shape: { type: 'Bpmn', shape: 'Activity', activity: { activity: 'Task', task: { type: 'User' } } } },
{ id: 'approve', offsetX: 430, offsetY: 200, width: 60, height: 60,
annotations: [{ content: 'Approved?', offset: { x: 0.5, y: 1.4 } }],
shape: { type: 'Bpmn', shape: 'Gateway', gateway: { type: 'Exclusive' } } },
{ id: 'end', offsetX: 580, offsetY: 200, width: 50, height: 50,
shape: { type: 'Bpmn', shape: 'Event', event: { event: 'End', trigger: 'None' } } }
];
const connectors: ConnectorModel[] = [
{ id: 'c1', sourceID: 'start', targetID: 'review', type: 'Orthogonal',
shape: { type: 'Bpmn', flow: 'Sequence', sequence: 'Normal' } },
{ id: 'c2', sourceID: 'review', targetID: 'approve', type: 'Orthogonal',
shape: { type: 'Bpmn', flow: 'Sequence', sequence: 'Normal' } },
{ id: 'c3', sourceID: 'approve', targetID: 'end', type: 'Orthogonal',
annotations: [{ content: 'Yes' }],
shape: { type: 'Bpmn', flow: 'Sequence', sequence: 'Conditional' } }
];
export default function App() {
return (
<DiagramComponent id="bpmn" width={'100%'} height={'400px'} nodes={nodes} connectors={connectors}>
<Inject services={[BpmnDiagrams]} />
</DiagramComponent>
);
}---
Shape Reference
shape.shape | Type | Use |
|---|---|---|
'Event' | Node | Start, Intermediate, End with triggers |
'Activity' | Node | Task, SubProcess |
'Gateway' | Node | Exclusive, Inclusive, Parallel etc. |
'DataObject' | Node | Input/Output data |
'DataSource' | Node | Database/data store |
'Group' | Node | Grouping boundary |
'TextAnnotation' | Node | Explanatory note |
'Message' | Node | Message indicator |
---
Troubleshooting
BPMN shapes not rendering
- Confirm
<Inject services={[BpmnDiagrams]} />is a child of<DiagramComponent>
Connector flow decorators look wrong
- Verify
shape.type: 'Bpmn'andshape.flowis'Sequence','Association', or'Message'
Gateway not showing type icon
- Set
gateway.typeexplicitly; default is'None'(empty diamond)
Related docs:
- Shapes & Styles → shapes-and-styles.md
- Connectors → connectors.md
Connectors in Syncfusion React Diagram
Table of Contents
- Connector Fundamentals
- Creating Connectors
- Connector Types
- Connecting Nodes
- Connecting via Ports
- Decorator Customization
- Connector Style
- getConnectorDefaults Pattern
- Runtime Connector Operations
- Line Routing
- Connector Events
- Advanced Properties
- Troubleshooting
---
Connector Fundamentals
Connectors are lines/arrows that link two points, nodes, or ports. Three things uniquely describe a connector:
| Concern | Properties |
|---|---|
| Endpoints | sourcePoint/targetPoint (coordinates) or sourceID/targetID (node IDs) |
| Path type | type: Straight \ |
| Appearance | style, sourceDecorator, targetDecorator |
ID rules: Connector IDs must start with a letter. No spaces, underscores, or special characters.
---
Creating Connectors
Freestanding connector (point-to-point)
import { DiagramComponent, ConnectorModel } from '@syncfusion/ej2-react-diagrams';
const connectors: ConnectorModel[] = [
{
id: 'c1',
sourcePoint: { x: 100, y: 100 },
targetPoint: { x: 300, y: 250 }
}
];
export default function App() {
return (
<DiagramComponent id="diagram" width={'100%'} height={'400px'} connectors={connectors} />
);
}Node-to-node connector
const connectors: ConnectorModel[] = [
{
id: 'c1',
sourceID: 'node1', // matches a node's id
targetID: 'node2', // matches a node's id
type: 'Orthogonal'
}
];---
Connector Types
Choose the type property based on the visual style you need:
| Type | Description | Best for |
|---|---|---|
Straight | Direct line between endpoints | Simple relationships, entity diagrams |
Orthogonal | Right-angle path (default routing) | Flowcharts, org charts, structured diagrams |
Bezier | Smooth curved path | Mind maps, creative diagrams |
Straight connector
const connector: ConnectorModel = {
id: 'straight1',
type: 'Straight',
sourcePoint: { x: 100, y: 100 },
targetPoint: { x: 300, y: 200 }
};Orthogonal connector
Routes through right angles automatically. The diagram engine calculates the path to avoid overlapping nodes when LineRouting is enabled.
const connector: ConnectorModel = {
id: 'ortho1',
type: 'Orthogonal',
sourceID: 'nodeA',
targetID: 'nodeB',
// Optional: explicit segments override auto-routing
segments: [
{ type: 'Orthogonal', direction: 'Right', length: 80 },
{ type: 'Orthogonal', direction: 'Bottom', length: 100 }
]
};Bezier connector
const connector: ConnectorModel = {
id: 'bezier1',
type: 'Bezier',
sourcePoint: { x: 100, y: 200 },
targetPoint: { x: 400, y: 200 },
// Optional: control points for curvature
segments: [
{
type: 'Bezier',
point1: { x: 200, y: 100 }, // first control point
point2: { x: 300, y: 300 } // second control point
}
]
};Corner radius (Orthogonal only)
Rounds the corners on orthogonal connectors for a softer look:
const connector: ConnectorModel = {
id: 'rounded',
type: 'Orthogonal',
sourceID: 'nodeA',
targetID: 'nodeB',
cornerRadius: 10 // pixels
};Apply globally via getConnectorDefaults:
getConnectorDefaults={(obj: ConnectorModel) => {
obj.type = 'Orthogonal';
obj.cornerRadius = 8;
return obj;
}}---
Connecting Nodes
Basic node-to-node connection
import { DiagramComponent, NodeModel, ConnectorModel } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [
{ id: 'start', offsetX: 150, offsetY: 150, width: 120, height: 50,
shape: { type: 'Flow', shape: 'Terminator' },
annotations: [{ content: 'Start' }] },
{ id: 'process', offsetX: 150, offsetY: 280, width: 120, height: 50,
shape: { type: 'Flow', shape: 'Process' },
annotations: [{ content: 'Process' }] }
];
const connectors: ConnectorModel[] = [
{ id: 'c1', sourceID: 'start', targetID: 'process', type: 'Orthogonal' }
];Restricting incoming/outgoing connections
Remove InConnect to prevent incoming connections to a node; remove OutConnect to block outgoing connections:
import { NodeConstraints } from '@syncfusion/ej2-react-diagrams';
const node: NodeModel = {
id: 'sourceOnly',
// This node can only be a source — no connectors can point TO it
constraints: NodeConstraints.Default & ~NodeConstraints.InConnect
};---
Connecting via Ports
Use sourcePortID and targetPortID to anchor connectors at specific named ports on nodes instead of the node center.
import { NodeModel, ConnectorModel, PointPortModel, PortVisibility } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [
{
id: 'node1',
offsetX: 150, offsetY: 150, width: 100, height: 100,
ports: [
{ id: 'rightPort', offset: { x: 1, y: 0.5 },
visibility: PortVisibility.Visible,
shape: 'Circle', style: { fill: '#366F8C' } }
]
},
{
id: 'node2',
offsetX: 350, offsetY: 150, width: 100, height: 100,
ports: [
{ id: 'leftPort', offset: { x: 0, y: 0.5 },
visibility: PortVisibility.Visible,
shape: 'Circle', style: { fill: '#366F8C' } }
]
}
];
const connectors: ConnectorModel[] = [
{
id: 'portConnector',
sourceID: 'node1',
targetID: 'node2',
sourcePortID: 'rightPort', // connect from right side of node1
targetPortID: 'leftPort', // connect to left side of node2
type: 'Straight'
}
];Change port connection at runtime
const diagramRef = useRef<DiagramComponent>(null);
const reconnect = () => {
const connector = diagramRef.current!.connectors[0];
connector.sourcePortID = 'bottomPort';
connector.targetPortID = 'topPort';
diagramRef.current!.dataBind();
};---
Decorator Customization
Decorators are the shapes at the source and target ends (arrows, circles, diamonds, etc.).
Available decorator shapes
None | Arrow | OpenArrow | Circle | Square | Diamond | IndentedArrow | OutdentedArrow | DoubleArrow | Custom
Common decorator configurations
const connector: ConnectorModel = {
id: 'c1',
sourcePoint: { x: 100, y: 150 },
targetPoint: { x: 350, y: 150 },
// No arrow at source end
sourceDecorator: { shape: 'None' },
// Styled arrow at target end
targetDecorator: {
shape: 'Arrow',
width: 12,
height: 12,
style: {
fill: '#2196F3',
strokeColor: '#2196F3'
}
}
};Circle source + custom target
const connector: ConnectorModel = {
id: 'c2',
type: 'Straight',
sourcePoint: { x: 100, y: 100 },
targetPoint: { x: 300, y: 200 },
sourceDecorator: { shape: 'Circle' },
targetDecorator: {
shape: 'Custom',
pathData: 'M0,0 L10,5 L0,10 Z' // SVG path for custom arrowhead
}
};Diamond decorator (typical for class diagrams)
targetDecorator: { shape: 'Diamond', width: 14, height: 14 }---
Connector Style
Stroke properties
const connector: ConnectorModel = {
id: 'styled',
sourcePoint: { x: 100, y: 100 },
targetPoint: { x: 300, y: 200 },
style: {
strokeColor: '#1565C0', // line color
strokeWidth: 2, // line thickness
strokeDashArray: '8 4', // dashed: "dashLen gapLen"
opacity: 0.8 // 0–1
}
};Dashed line patterns
| Pattern | strokeDashArray |
|---|---|
| Solid | (omit / empty string) |
| Dashed | '8 4' |
| Dotted | '2 3' |
| Long dash | '12 4' |
| Dash-dot | '8 3 2 3' |
---
getConnectorDefaults Pattern
getConnectorDefaults runs for every connector before rendering. Use it to enforce a consistent style across all connectors without repeating properties:
<DiagramComponent
id="diagram"
width={'100%'} height={'500px'}
nodes={nodes}
connectors={connectors}
getConnectorDefaults={(obj: ConnectorModel): ConnectorModel => {
obj.type = 'Orthogonal';
obj.cornerRadius = 8;
obj.targetDecorator = { shape: 'Arrow', width: 10, height: 10 };
obj.style = { strokeColor: '#455A64', strokeWidth: 1.5 };
return obj;
}}
/>Org-chart / layout defaults (minimal decorators)
getConnectorDefaults={(obj: ConnectorModel): ConnectorModel => {
obj.type = 'Orthogonal';
obj.targetDecorator = { shape: 'None' }; // no arrows in org charts
obj.style = {
strokeColor: '#6BA5D7',
strokeWidth: 2
};
return obj;
}}---
Runtime Connector Operations
Add a connector
const diagramRef = useRef<DiagramComponent>(null);
const addConnector = () => {
const newConnector: ConnectorModel = {
id: 'dynamic1',
sourceID: 'nodeA',
targetID: 'nodeC',
type: 'Orthogonal'
};
diagramRef.current!.add(newConnector);
};Remove a connector
const removeConnector = () => {
const connector = diagramRef.current!.getObject('dynamic1');
diagramRef.current!.remove(connector);
};Add multiple connectors at once
const batch: ConnectorModel[] = [
{ id: 'c1', sourcePoint: { x: 80, y: 80 }, targetPoint: { x: 150, y: 150 } },
{ id: 'c2', type: 'Orthogonal', sourcePoint: { x: 170, y: 170 }, targetPoint: { x: 300, y: 300 } }
];
diagramRef.current!.addElements(batch);Update connector properties at runtime
const updateConnector = () => {
const c = diagramRef.current!.connectors[0];
c.style.strokeColor = '#E53935';
c.style.strokeWidth = 3;
c.targetDecorator.style.fill = '#E53935';
c.sourcePoint.x = 150;
diagramRef.current!.dataBind(); // required to reflect changes
};Clone a connector
const cloneConnector = () => {
const diagram = diagramRef.current!;
diagram.select([diagram.connectors[0]]);
diagram.copy();
diagram.paste();
};---
Line Routing
Line routing automatically re-routes orthogonal connectors around nodes so they don't overlap.
Enable line routing
Inject LineRouting and add its constraint to the diagram:
import { Diagram, DiagramComponent, LineRouting, DiagramConstraints } from '@syncfusion/ej2-react-diagrams';
// Inject the module (outside the component, once)
Diagram.Inject(LineRouting);
export default function App() {
return (
<DiagramComponent
id="diagram"
width={'100%'} height={'600px'}
nodes={nodes}
connectors={connectors}
constraints={DiagramConstraints.Default | DiagramConstraints.LineRouting}
/>
);
}Disable routing on a specific connector
import { ConnectorConstraints } from '@syncfusion/ej2-react-diagrams';
const connector: ConnectorModel = {
id: 'manual',
sourceID: 'nodeA', targetID: 'nodeB',
type: 'Orthogonal',
// Opt this connector out of auto-routing
constraints: ConnectorConstraints.Default & ~ConnectorConstraints.InheritLineRouting
};Avoid line overlapping
Prevents multiple orthogonal connectors from stacking on top of each other. Requires LineRouting:
import { Diagram, DiagramComponent, LineRouting, AvoidLineOverlapping, DiagramConstraints } from '@syncfusion/ej2-react-diagrams';
Diagram.Inject(LineRouting, AvoidLineOverlapping);
<DiagramComponent
constraints={
DiagramConstraints.Default |
DiagramConstraints.LineRouting |
DiagramConstraints.AvoidLineOverlapping
}
/>AvoidLineOverlappingonly works withOrthogonalconnectors and requiresLineRoutingto be active.
---
Connector Events
All events are props on DiagramComponent. Use args.state to differentiate 'Changing' (cancelable) from 'Changed' (completed).
Click
import { IClickEventArgs, Connector } from '@syncfusion/ej2-react-diagrams';
<DiagramComponent
click={(args: IClickEventArgs) => {
if (args.actualObject instanceof Connector) {
console.log('Connector clicked:', (args.actualObject as ConnectorModel).id);
}
}}
/>Connection change (connector endpoint changed)
Fires when a connector's source or target is changed by dragging its endpoint onto a different node/port.
connectionChange={(args: IConnectionChangeEventArgs) => {
if (args.state === 'Changed') {
console.log('New target:', args.connector.targetID);
}
// Prevent the reconnection:
// if (args.state === 'Changing') args.cancel = true;
}}Segment change (connector path reshaping)
Fires when a user drags a segment of an editable connector.
segmentChange={(args: ISegmentChangeEventArgs) => {
if (args.state === 'Completed') {
console.log('Segments updated');
}
}}Position change (connector drag)
positionChange={(args: IDraggingEventArgs) => {
if (args.state === 'Completed') {
console.log('Connector moved');
}
// Prevent drag: if (args.state === 'Progress') args.cancel = true;
}}Collection change (connector added/removed)
collectionChange={(args: ICollectionChangeEventArgs) => {
if (args.state === 'Changed') {
console.log('Connector collection changed');
}
// Prevent: if (args.state === 'Changing') args.cancel = true;
}}---
Advanced Properties
| Property | Type | Description |
|---|---|---|
type | `'Straight' \ | 'Orthogonal' \ |
segments | ConnectorSegmentModel[] | Manual path segment definitions |
sourceDecorator | DecoratorModel | Shape at the source end |
targetDecorator | DecoratorModel | Shape at the target end |
cornerRadius | number | Rounds orthogonal connector corners |
sourceID / targetID | string | Connect to nodes by ID |
sourcePortID / targetPortID | string | Connect to specific ports by ID |
sourcePoint / targetPoint | PointModel | Freestanding endpoint coordinates |
constraints | ConnectorConstraints | Enable/disable edit, select, drag, routing |
bridgeSpace | number | Gap size when bridge (arc) is drawn over crossing connectors |
addInfo | any | Custom metadata attached to the connector |
tooltip | DiagramTooltipModel | Tooltip shown on hover |
annotations | PathAnnotationModel[] | Text labels along the connector path |
---
Troubleshooting
Connector not connecting to a node
- Confirm
sourceID/targetIDmatch the exactidvalues of the target nodes - Check that
NodeConstraints.InConnect/OutConnectare not removed from the node
Port connection not working
- Ensure the
idvalues insourcePortID/targetPortIDmatch ports defined in the node'sportsarray - Verify ports have
visibility: PortVisibility.Visibleif you need them shown
Orthogonal connector not routing around nodes
- Inject
LineRoutingviaDiagram.Inject(LineRouting)(not inside the component) - Add
DiagramConstraints.LineRoutingto theconstraintsprop onDiagramComponent
Runtime style change not reflecting
- Call
diagramRef.current!.dataBind()after mutating connector properties directly
`AvoidLineOverlapping` not working
- Check that
LineRoutingis also injected and its constraint is enabled - This feature only applies to
Orthogonalconnectors
Related docs:
- Ports → ports.md
- Labels on connectors → labels-and-annotations.md
- Bezier details → see
connector-bezier.mdin docs/ - Connector segments → see
connector-segments.mdin docs/
Data Binding in Syncfusion React Diagram
Table of Contents
- Data Binding Overview
- Required Setup
- dataSourceSettings Reference
- Local Data Binding
- Remote Data Binding
- doBinding — Customizing Node Appearance from Data
- setNodeTemplate — Rich Visual Templates
- Separate Node and Connector Data Sources
- PostgreSQL / External Database
- CRUD Operations
- Best Practices
- Troubleshooting
---
Data Binding Overview
Data binding in the Syncfusion React Diagram automatically generates nodes and connectors from a structured data source. Instead of defining each node manually, provide a JSON array (or remote URL) with parent–child relationships and the component builds the diagram.
When to use data binding:
- Rendering org charts or hierarchical structures from database data
- Displaying dynamic process flows where data changes at runtime
- Avoiding manual node/connector definition for large diagrams
Data binding always works in combination with a layout (e.g., HierarchicalTree, OrganizationalChart, Flowchart). The layout determines how auto-generated nodes are positioned.
---
Required Setup
Inject DataBinding alongside the desired layout module:
import {
DiagramComponent, Inject,
DataBinding, HierarchicalTree
} from '@syncfusion/ej2-react-diagrams';
import { DataManager } from '@syncfusion/ej2-data';
<Inject services={[DataBinding, HierarchicalTree]} />---
dataSourceSettings Reference
| Property | Type | Description |
|---|---|---|
id | string | Field name in the data that uniquely identifies each record |
parentId | string | Field name that points to the parent record's id |
dataSource | DataManager \ | object[] |
dataManager | DataManager | Alternative to dataSource for DataManager instances |
root | string | Value of the id field that is the root node (optional; auto-detected when omitted) |
doBinding | function | Callback invoked per node to map data fields to node properties |
connectionDataSource | ConnectionDataSourceModel | Separate data source for explicit connectors |
crudAction | CrudActionModel | Endpoints for CRUD operations |
customFields | string[] | Extra data fields to carry through to node's data property |
---
Local Data Binding
Bind a client-side JSON array to the diagram. Each record represents a node; parent–child relationships are established through id / parentId mappings.
import * as React from 'react';
import {
DiagramComponent, Inject,
DataBinding, HierarchicalTree,
NodeModel, ConnectorModel
} from '@syncfusion/ej2-react-diagrams';
import { DataManager } from '@syncfusion/ej2-data';
const employeeData = [
{ Name: 'CEO', fillColor: '#3DD94A' },
{ Name: 'VP-Eng', Category: 'CEO' },
{ Name: 'VP-Sales', Category: 'CEO' },
{ Name: 'Lead Dev', Category: 'VP-Eng' },
{ Name: 'Sales Mgr', Category: 'VP-Sales' },
];
export default function App() {
return (
<DiagramComponent
id="container"
width="100%" height="500px"
dataSourceSettings={{
id: 'Name',
parentId: 'Category',
dataManager: new DataManager(employeeData as JSON[]),
}}
layout={{ type: 'HierarchicalTree', horizontalSpacing: 30, verticalSpacing: 50 }}
getNodeDefaults={(node: NodeModel) => {
node.width = 100; node.height = 40;
node.style = { fill: '#ffeec7', strokeColor: '#f5d897' };
return node;
}}
getConnectorDefaults={(c: ConnectorModel) => {
c.type = 'Orthogonal';
(c.targetDecorator as any).shape = 'None';
return c;
}}
>
<Inject services={[DataBinding, HierarchicalTree]} />
</DiagramComponent>
);
}Data rules:
- The root record has no
parentIdvalue (null or undefined). idvalues must be unique within the dataset.parentIdmust reference an existingid; orphaned references are ignored.
---
Remote Data Binding
Fetch data from a REST endpoint using DataManager with a URL adapter:
import { DataManager } from '@syncfusion/ej2-data';
<DiagramComponent
dataSourceSettings={{
id: 'Id',
parentId: 'ParentId',
dataSource: new DataManager({
url: "[YOUR URL HERE]",
crossDomain: true,
}),
doBinding: (nodeModel: NodeModel, data: any) => {
nodeModel.annotations = [{ content: data.Label, style: { color: 'white' } }];
},
}}
layout={{ type: 'OrganizationalChart' }}
getNodeDefaults={(node: NodeModel) => {
node.width = 80; node.height = 40;
node.style = { fill: '#048785', strokeColor: 'transparent' };
return node;
}}
getConnectorDefaults={(c: ConnectorModel) => {
c.type = 'Orthogonal';
(c.targetDecorator as any).shape = 'None';
return c;
}}
>
<Inject services={[DataBinding, HierarchicalTree]} />
</DiagramComponent>Remote data binding fires the dataLoaded layout event when data finishes loading.---
doBinding — Customizing Node Appearance from Data
doBinding is called once per node during diagram initialization. Use it to map data fields directly to node model properties:
dataSourceSettings={{
id: 'Id',
parentId: 'Team',
dataManager: items,
doBinding: (nodeModel: NodeModel, data: any, diagram: Diagram) => {
// Set annotation content from data
nodeModel.annotations = [{
content: data.Role,
style: { color: 'white', fontSize: 12 },
}];
// Conditionally style based on data
if (data.Level === 'Executive') {
nodeModel.style = { fill: '#c34444', strokeColor: 'white' };
} else {
nodeModel.style = { fill: '#3c63ac', strokeColor: 'white' };
}
},
}}doBinding receives:
nodeModel— theNodeModelbeing configured (mutable)data— the raw data record from the data sourcediagram— the diagram instance
---
setNodeTemplate — Rich Visual Templates
For complex node visuals (image + text, multi-row cards), use setNodeTemplate instead of or in addition to doBinding:
import {
StackPanel, ImageElement, TextElement, Container
} from '@syncfusion/ej2-react-diagrams';
function setNodeTemplate(node: NodeModel): Container {
const panel = new StackPanel();
panel.width = 200;
panel.height = 60;
panel.orientation = 'Horizontal';
panel.style.fill = 'skyblue';
panel.cornerRadius = 8;
const avatar = new ImageElement();
avatar.width = 40;
avatar.height = 40;
avatar.margin = { left: 10, top: 10, right: 0, bottom: 0 };
const nameLabel = new TextElement();
nameLabel.content = (node.data as any).Name;
nameLabel.style.color = 'white';
nameLabel.margin = { left: 10, top: 5, right: 0, bottom: 0 };
panel.children = [avatar, nameLabel];
return panel;
}
<DiagramComponent setNodeTemplate={setNodeTemplate} ... />setNodeTemplate must return a Container (e.g., StackPanel). Elements inside can be:
TextElement— text labelsImageElement— images by URL or base64PathElement— SVG path shapesNativeElement— raw SVG contentDiagramElement— general-purpose elementsHtmlElement— embedded HTML
---
Separate Node and Connector Data Sources
When connector relationships are stored separately (e.g., a junction table), use connectionDataSource:
import { DataManager, ODataV4Adaptor } from '@syncfusion/ej2-data';
const nodeData = [
{ id: '1', label: 'Router' },
{ id: '2', label: 'Switch A' },
{ id: '3', label: 'Switch B' },
];
const connectorData = [
{ id: 'c1', sourceNodeId: '1', targetNodeId: '2' },
{ id: 'c2', sourceNodeId: '1', targetNodeId: '3' },
];
dataSourceSettings={{
id: 'id',
dataSource: new DataManager(nodeData as JSON[]),
connectionDataSource: {
id: 'id',
sourceID: 'sourceNodeId',
targetID: 'targetNodeId',
dataManager: new DataManager(connectorData as JSON[]),
},
}}WhenconnectionDataSourceis used, parent–child inferred connectors fromparentIdare overridden by the explicit connectors.
---
The diagram waits for the async fetch to complete before rendering. The dataLoaded event fires on completion.
---
CRUD Operations
The diagram supports bidirectional CRUD sync — when users modify nodes (add, update, delete) via UI interactions, changes can be pushed back to the server.
dataSourceSettings={{
id: 'id',
parentId: 'parentId',
dataSource: items,
crudAction: {
read: 'https://api.example.com/nodes',
create: 'https://api.example.com/nodes/create',
update: 'https://api.example.com/nodes/update',
destroy: 'https://api.example.com/nodes/delete',
customFields: ['Role', 'Department'], // extra fields to include in payloads
},
connectionDataSource: {
id: 'id',
sourceID: 'sourceNodeId',
targetID: 'targetNodeId',
crudAction: {
read: 'https://api.example.com/connectors',
create: 'https://api.example.com/connectors/create',
update: 'https://api.example.com/connectors/update',
destroy: 'https://api.example.com/connectors/delete',
},
},
}}---
Best Practices
- Always match field names exactly:
idandparentIdvalues indataSourceSettingsmust match the actual property names in your data objects (case-sensitive). - Root record: The root should have
parentIdasnullorundefined— not an empty string. - Use `doBinding` for appearance,
setNodeTemplatefor complex multi-element visuals. - Combine with layouts: Data binding is meaningless without a layout — always set
layout.typealongsidedataSourceSettings. - Remote data: Use
dataLoadedevent to trigger post-render customizations such as selecting a specific node or scrolling the viewport. - Custom fields: List extra data properties in
crudAction.customFieldsto ensure they are passed through to the node'sdataproperty and accessible indoBinding.
---
Troubleshooting
No nodes appear after data binding → Verify DataBinding module is injected. → Check that id and parentId field names match your data exactly. → Confirm the root record has no parentId value.
All nodes stack at the same position → The layout type is not set — dataSourceSettings requires a layout prop to position nodes.
`doBinding` not being called → Confirm DataBinding is in the injected services array; without it, doBinding is never invoked.
Remote data diagram appears empty → Check network requests in browser DevTools for API errors (CORS, 4xx/5xx responses). → Ensure the crossDomain: true option is set in the DataManager configuration.
`setNodeTemplate` returns `null` → Always return a valid Container instance (e.g., new StackPanel()). Returning null or undefined causes nodes to render with no content.
Diagram Settings
Table of Contents
- Layers
- Virtualization
- Grid Lines and Snapping
- Ruler
- Scroll Settings
- Page Settings
- Tooltip
- Overview Panel
- Localization
- Accessibility
- Best Practices
- Troubleshooting
---
Layers
Layers group diagram elements into named categories for selective visibility, locking, and z-order control. Each layer has id, objects (array of element IDs), visible, lock, zIndex, and addInfo properties.
Define Layers
<DiagramComponent
layers={[
{ id: 'layer1', objects: ['node1', 'node2'], visible: true, lock: false },
{ id: 'layer2', objects: ['node3', 'connector1'], visible: true, lock: true },
]}
/>Layer Properties
| Property | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique layer identifier |
objects | string[] | [] | IDs of nodes/connectors in this layer |
visible | boolean | true | Show/hide all elements in layer |
lock | boolean | false | Prevent selection and interaction |
zIndex | number | — | Stacking order (higher = in front) |
addInfo | object | — | Custom metadata attached to the layer |
Runtime Layer Methods
// Add a new layer with optional objects
diagramInstance.addLayer({ id: 'newLayer', visible: true, lock: false }, [
{ id: 'con1', type: 'Straight', sourceID: 'node1', targetID: 'node2' }
]);
// Remove a layer by ID
diagramInstance.removeLayer('layer1');
// Move objects from one layer to another
// Parameter 1 - An array of object IDs represented as strings to be moved
// parameter 2 - The ID of the target layer to which the objects should be moved.
diagramInstance.moveObjects(['node1', 'node2'], 'layer2');
// Clone a layer with all its elements
diagramInstance.cloneLayer('layer1');
// Z-order control
diagramInstance.bringLayerForward('layer1');
diagramInstance.sendLayerBackward('layer1');
// Active layer management
diagramInstance.getActiveLayer(); // Returns current active layer
diagramInstance.setActiveLayer('layer2'); // New objects are added to this layerThe active layer is the layer with the highest z-index. New runtime-added objects are assigned to the active layer automatically.
---
Virtualization
Enable virtualization for large diagrams (100+ nodes/connectors) to render only visible elements, significantly improving performance.
import { DiagramComponent, DiagramConstraints } from '@syncfusion/ej2-react-diagrams';
<DiagramComponent
constraints={DiagramConstraints.Default | DiagramConstraints.Virtualization}
/>Virtualization provides:
- Reduced memory usage (only visible objects loaded)
- Faster initial rendering
- Smooth scrolling and panning
- Consistent performance as diagram size grows
---
Grid Lines and Snapping
Grid lines and snapping require injecting the Snapping module. Configure via snapSettings.
import { DiagramComponent, SnapConstraints, SnapSettingsModel } from '@syncfusion/ej2-react-diagrams';
import { Snapping } from '@syncfusion/ej2-react-diagrams';
const snapSettings: SnapSettingsModel = {
constraints: SnapConstraints.ShowLines | SnapConstraints.SnapToLines,
horizontalGridlines: { lineColor: '#e0e0e0', lineDashArray: '2 2' },
verticalGridlines: { lineColor: '#e0e0e0', lineDashArray: '2 2' },
snapObjectDistance: 5,
snapAngle: 5,
snapLineColor: '#07EDE1',
};
<DiagramComponent snapSettings={snapSettings} />Snap Constraints
| Constraint | Description |
|---|---|
SnapConstraints.ShowLines | Display grid lines |
SnapConstraints.SnapToLines | Snap elements to grid intersections |
SnapConstraints.SnapToObject | Snap to neighboring elements with smart guides |
SnapConstraints.All | Enable all snapping features (default) |
SnapConstraints.None | Disable all grid and snapping |
Dot Grid Pattern
const snapSettings: SnapSettingsModel = {
constraints: SnapConstraints.ShowLines,
gridType: 'Dots', // Default is 'Lines'
horizontalGridlines: { dotIntervals: [3, 20, 1, 20], lineColor: 'blue' },
verticalGridlines: { dotIntervals: [3, 20, 1, 20], lineColor: 'blue' },
};Custom Snap Interval
// Objects snap every 10px instead of to nearest gridline
horizontalGridlines: { snapIntervals: [10] },
verticalGridlines: { snapIntervals: [10] },---
Ruler
Display horizontal and vertical measurement guides with rulerSettings.
<DiagramComponent
rulerSettings={{
showRulers: true,
horizontalRuler: {
interval: 8,
segmentWidth: 100,
thickness: 35,
tickAlignment: 'RightOrBottom',
markerColor: 'red',
},
verticalRuler: {
interval: 10,
segmentWidth: 200,
thickness: 35,
tickAlignment: 'LeftOrTop',
},
}}
/>| Property | Description |
|---|---|
showRulers | Toggle ruler visibility |
interval | Spacing between tick marks |
segmentWidth | Width of each ruler segment |
thickness | Ruler display area height/width |
tickAlignment | 'LeftOrTop' \ |
markerColor | Color of the cursor position indicator (default: red) |
arrangeTick | Callback function to customize individual tick appearance |
---
Scroll Settings
Control scroll behavior, viewport, zoom limits, and auto-scroll via scrollSettings.
<DiagramComponent
scrollSettings={{
scrollLimit: 'Infinity', // 'Infinity' | 'Diagram' | 'Limited'
canAutoScroll: true,
autoScrollBorder: { left: 15, right: 15, top: 15, bottom: 15 },
autoScrollFrequency: 50, // ms between auto-scroll ticks
padding: { left: 100, top: 100 },
scrollableArea: new Rect(0, 0, 2000, 2000), // used with scrollLimit:'Limited'
minZoom: 0.2,
maxZoom: 5,
}}
/>Programmatic Scroll and Zoom
// Set initial scroll position
diagramInstance.scrollSettings.horizontalOffset = 100;
diagramInstance.scrollSettings.verticalOffset = 100;
diagramInstance.dataBind();
// Zoom programmatically
diagramInstance.zoomTo({ type: 'ZoomIn', zoomFactor: 0.2, focusPoint: { x: 0.5, y: 0.5 } });
diagramInstance.zoomTo({ type: 'ZoomOut', zoomFactor: 0.2 });
// Reset zoom and scroll to defaults
diagramInstance.reset();
// Update viewport after container resize
diagramInstance.updateViewPort();Scroll Change Event
<DiagramComponent
scrollChange={(args) => {
console.log(args.panState, args.ScrollOffset);
}}
/>ForcanAutoScrollto work,scrollLimitmust be set to'Infinity'.
---
Page Settings
Configure page size, orientation, background, margins, multiple pages, and boundary constraints.
<DiagramComponent
pageSettings={{
width: 816,
height: 1056,
orientation: 'Portrait', // 'Portrait' | 'Landscape'
multiplePage: true,
showPageBreaks: true,
boundaryConstraints: 'Page', // 'Infinity' | 'Diagram' | 'Page'
background: {
color: '#f5f5f5',
source: 'https://example.com/bg.jpg', // optional background image
scale: 'Meet',
align: 'XMinYMin',
},
margin: { left: 10, top: 10, right: 10, bottom: 10 },
fitOptions: {
canFit: true,
canZoomIn: true,
region: 'Content', // 'Content' | 'PageSettings' | 'CustomBounds'
mode: 'Page', // 'Page' | 'Width' | 'Height'
margin: { left: 50, right: 50, top: 50, bottom: 50 },
},
}}
/>| Property | Description |
|---|---|
width / height | Page dimensions in pixels |
orientation | Swaps width/height when changed |
multiplePage | Extend canvas across multiple pages |
showPageBreaks | Render visual page break lines |
boundaryConstraints | 'Infinity' (no restriction) \ |
background.color | Page background fill color |
background.source | Background image URL |
fitOptions.canFit | Center content in viewport on load |
---
Tooltip
Default Interaction Tooltip
The diagram displays position/size/angle during drag, resize, and rotate automatically. Disable with:
import { SelectorConstraints } from '@syncfusion/ej2-react-diagrams';
<DiagramComponent
selectedItems={{ constraints: SelectorConstraints.All & ~SelectorConstraints.ToolTip }}
/>Diagram-Level Tooltip (Mouse Hover)
import { DiagramConstraints } from '@syncfusion/ej2-react-diagrams';
<DiagramComponent
constraints={DiagramConstraints.Default | DiagramConstraints.Tooltip}
tooltip={{ content: 'Diagram tooltip', position: 'BottomRight' }}
/>Per-Node/Connector Tooltip
import { NodeConstraints, ConnectorConstraints } from '@syncfusion/ej2-react-diagrams';
// Node with tooltip
{
id: 'node1',
constraints: NodeConstraints.Default | NodeConstraints.Tooltip,
tooltip: {
content: 'My Node',
position: 'BottomRight',
relativeMode: 'Object', // 'Object' | 'Mouse'
isSticky: false, // Keep visible after mouse-out
showTipPointer: true,
width: 200,
height: 60,
animation: {
open: { effect: 'ZoomIn', duration: 300, delay: 0 },
close: { effect: 'ZoomOut', duration: 200, delay: 0 },
},
},
}
// Port tooltip
{
constraints: PortConstraints.Default | PortConstraints.ToolTip,
tooltip: { content: 'Port tooltip' },
}
// Annotation tooltip
{
constraints: AnnotationConstraints.Tooltip,
tooltip: { content: 'Annotation tooltip', position: 'TopRight', relativeMode: 'Object' },
}Programmatic Tooltip Control
diagramInstance.showTooltip(diagramInstance.nodes[0]);
diagramInstance.hideTooltip(diagramInstance.nodes[0]);
// Use openOn: 'Custom' on tooltip to prevent auto-hover trigger---
Overview Panel
Display a miniature preview of the full diagram for navigation. The overview viewport rectangle supports drag, resize, and click to navigate.
import { OverviewComponent } from '@syncfusion/ej2-react-diagrams';
// In your component render:
<div style={{ display: 'flex' }}>
<DiagramComponent id="container" width={'70%'} height={'600px'} />
<OverviewComponent
id="overview"
sourceID="container" // Must match DiagramComponent id
width={'300px'}
height={'150px'}
/>
</div>Overview interactions:
- Drag the viewport rectangle to pan the main diagram
- Resize the rectangle to zoom in/out
- Click a location to navigate instantly
- Click and drag to define a new viewport region
---
Localization
Localize context menu items and symbol palette search text using L10n.load() and setCulture().
import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent,DiagramContextMenu,Diagram,NodeModel } from "@syncfusion/ej2-react-diagrams";
Diagram.Inject(DiagramContextMenu);
import { L10n, setCulture } from '@syncfusion/ej2-base';
setCulture('de');
L10n.load({
'de-DE': {
diagram: {
Cut: 'Corte',
Copy: 'Copia',
Paste: 'Pasta',
Undo: 'Deshacer',
Redo: 'Rehacer',
SelectAll: 'Seleccionar todo',
Grouping: 'Agrupación',
Group: 'Grupo',
Ungroup: 'Desagrupar',
Order: 'Fin',
BringToFront: 'Traer a delante',
MoveForward: 'Movimiento adelante',
SendToBack: 'Enviar a espalda',
SendBackward: 'Enviar hacia atrás',
},
},
});
let node:NodeModel [] = [
{
id: 'Node1',
offsetX: 300,
offsetY: 288,
annotations: [{ content: 'Node1' }],
},
{
id: 'Node2',
offsetX: 150,
offsetY: 250,
annotations: [{ content: 'Node2' }],
},
];
function App() {
return (<DiagramComponent id="container" width={'100%'} height={'600px'}
locale='de-DE'
//Enables the context menu
contextMenuSettings={{
show: true
}}
nodes={node}
getNodeDefaults={(node:NodeModel) => {
node.width = 100;
node.height = 100;
node.shape = { type: 'Basic', shape: 'Ellipse' };
}}
/>);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);Uselocaleprop on bothDiagramComponentandSymbolPaletteComponentindependently.
---
Accessibility
The diagram provides WAI-ARIA compliance through aria-label attributes on interactive resize/rotate thumbs and connector endpoints.
Accessibility Compliance Summary
| Feature | Support |
|---|---|
| WCAG 2.2 | Partial |
| Screen Reader | ✅ Full |
| Color Contrast | ✅ Full |
| Mobile Device | ✅ Full |
| Keyboard Navigation | Partial |
| Right-to-Left | ❌ Not supported |
Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Ctrl+A | Select all |
Ctrl+X / Ctrl+C / Ctrl+V | Cut / Copy / Paste |
Ctrl+Z / Ctrl+Y | Undo / Redo |
Delete | Delete selected |
| Arrow keys | Nudge selected object |
Enter | Start annotation edit |
Escape | End annotation edit |
Ctrl++ / Ctrl+- | Zoom in / Zoom out |
Ctrl+mouse wheel | Zoom in/out |
---
Best Practices
- Use layers to separate background template elements (locked) from editable content layers.
- Enable virtualization for any diagram with 100+ elements to maintain responsive performance.
- Set
preventDefaults: trueinserializationSettingswhen layers add many nodes to reduce load time. - Use
scrollLimit: 'Diagram'for fixed-size diagrams; use'Infinity'for open-ended canvases. - Enable
showPageBreaks: truewhenever usingmultiplePage: trueto give visual layout guidance.
Troubleshooting
| Issue | Solution |
|---|---|
| Locked layer elements still appear draggable | Confirm lock: true is set on the layer, not just the node |
| Virtualization causes elements to disappear | Ensure DiagramConstraints.Virtualization is OR'd with DiagramConstraints.Default |
| Grid lines not visible | Inject Snapping module; set snapSettings.constraints to include SnapConstraints.ShowLines |
| Auto-scroll not triggering | canAutoScroll: true requires scrollLimit: 'Infinity' |
| Overview not reflecting diagram | Verify sourceID on OverviewComponent exactly matches the id on DiagramComponent |
| Context menu not localized | Call setCulture() before rendering; set locale prop on the component |
Getting Started with Syncfusion React Diagram
Table of Contents
- Dependencies
- Installation
- CSS Theme Imports
- Basic DiagramComponent Setup
- Module Injection
- Core Diagram Elements
- Next.js Integration
- Preact Integration
- Troubleshooting
---
Dependencies
The Diagram component requires these peer packages (all installed automatically with the main package):
@syncfusion/ej2-react-diagrams
├── @syncfusion/ej2-base
├── @syncfusion/ej2-data
├── @syncfusion/ej2-navigations
├── @syncfusion/ej2-inputs
├── @syncfusion/ej2-popups
├── @syncfusion/ej2-buttons
├── @syncfusion/ej2-lists
├── @syncfusion/ej2-splitbuttons
├── @syncfusion/ej2-diagrams
└── @syncfusion/ej2-react-base---
Installation
Vite + React (recommended)
# Create project
npm create vite@latest my-app -- --template react-ts
cd my-app
# Install Syncfusion Diagram package
npm install @syncfusion/ej2-react-diagrams --save
npm run devCreate React App
npx create-react-app my-app --template typescript
cd my-app
npm install @syncfusion/ej2-react-diagrams --save
npm start---
CSS Theme Imports
Import CSS in your root stylesheet (src/App.css or src/index.css). Choose one theme:
Material (standard)
@import "../node_modules/@syncfusion/ej2-base/styles/material.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material.css";
@import "../node_modules/@syncfusion/ej2-react-diagrams/styles/material.css";Material 3
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-react-diagrams/styles/material3.css";Bootstrap 5
@import "../node_modules/@syncfusion/ej2-base/styles/bootstrap5.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/bootstrap5.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/bootstrap5.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/bootstrap5.css";
@import "../node_modules/@syncfusion/ej2-react-diagrams/styles/bootstrap5.css";Import order matters — base styles must come before component styles.
---
Basic DiagramComponent Setup
Minimal empty canvas
import * as React from 'react';
import { DiagramComponent } from '@syncfusion/ej2-react-diagrams';
import './App.css';
export default function App() {
return (
<DiagramComponent
id="diagram"
width={'100%'}
height={'500px'}
/>
);
}Diagram with nodes and connectors
import * as React from 'react';
import {
DiagramComponent,
NodeModel,
ConnectorModel,
Inject,
UndoRedo
} from '@syncfusion/ej2-react-diagrams';
import './App.css';
const nodes: NodeModel[] = [
{
id: 'node1',
offsetX: 200, offsetY: 150,
width: 120, height: 50,
annotations: [{ content: 'Node 1' }]
},
{
id: 'node2',
offsetX: 400, offsetY: 150,
width: 120, height: 50,
annotations: [{ content: 'Node 2' }]
}
];
const connectors: ConnectorModel[] = [
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2'
}
];
export default function App() {
return (
<DiagramComponent
id="diagram"
width={'100%'}
height={'400px'}
nodes={nodes}
connectors={connectors}
>
<Inject services={[UndoRedo]} />
</DiagramComponent>
);
}Important: id must be unique and letter-first
// ✅ Valid node IDs
id: 'start'
id: 'node1'
id: 'process-step'
// ❌ Invalid — starts with number or contains spaces
id: '1node'
id: 'my node'---
Module Injection
The Diagram component uses opt-in feature modules. Only inject what you use — this keeps bundle size minimal.
import {
DiagramComponent,
Inject,
// Add only the modules you need:
UndoRedo,
DataBinding,
HierarchicalTree,
MindMap,
RadialTree,
ComplexHierarchicalTree,
LayoutAnimation,
Snapping,
PrintAndExport,
BpmnDiagrams,
ConnectorBridging,
ConnectorEditing,
DiagramContextMenu,
Ej1Serialization
} from '@syncfusion/ej2-react-diagrams';
export default function App() {
return (
<DiagramComponent id="diagram" width={'100%'} height={'500px'}>
{/* Only inject what your diagram actually uses */}
<Inject services={[UndoRedo, Snapping]} />
</DiagramComponent>
);
}Module quick reference:
| Module | When to inject |
|---|---|
UndoRedo | Ctrl+Z / Ctrl+Y support |
Snapping | Grid snapping |
PrintAndExport | Export to image/SVG, print |
DataBinding | Render from data source |
HierarchicalTree | Tree / org-chart layouts |
MindMap | Mind map layouts |
RadialTree | Radial circular layouts |
ComplexHierarchicalTree | Multi-parent tree layouts |
LayoutAnimation | Animated layout transitions |
BpmnDiagrams | BPMN shape types |
DiagramContextMenu | Right-click menu |
ConnectorBridging | Arc bridges over crossing connectors |
ConnectorEditing | Drag-to-reshape connector segments |
Ej1Serialization | Load legacy EJ1 diagram JSON |
Forgetting to inject a required module is the #1 cause of "feature not working" issues. Always check module injection first.
---
Core Diagram Elements
Understanding the four building blocks helps you compose any diagram:
| Element | Purpose | Key Property |
|---|---|---|
| Node | Graphical shape (box, circle, flow shape) | nodes array |
| Connector | Line/arrow between nodes or points | connectors array |
| Annotation | Text label on a node or connector | annotations array on node/connector |
| Port | Named connection point on a node | ports array on node |
All elements use SVG rendering and are positioned via offsetX/offsetY coordinates from the canvas origin (top-left).
---
Next.js Integration
Next.js requires the 'use client' directive because DiagramComponent uses browser APIs.
Installation
npx create-next-app@latest ej2-nextjs-diagram
cd ej2-nextjs-diagram
npm install @syncfusion/ej2-react-diagrams --saveCSS import — add to src/app/globals.css
@import "../../node_modules/@syncfusion/ej2-base/styles/material.css";
@import "../../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import "../../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css";
@import "../../node_modules/@syncfusion/ej2-navigations/styles/material.css";
@import "../../node_modules/@syncfusion/ej2-react-diagrams/styles/material.css";Component — src/app/page.tsx
'use client' // Required — DiagramComponent uses browser APIs
import { DiagramComponent, NodeModel, Inject, UndoRedo } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [
{
id: 'start', offsetX: 200, offsetY: 150,
width: 120, height: 50,
annotations: [{ content: 'Start' }]
}
];
export default function Home() {
return (
<DiagramComponent id="diagram" width={'100%'} height={'500px'} nodes={nodes}>
<Inject services={[UndoRedo]} />
</DiagramComponent>
);
}Without'use client', Next.js will throw a hydration error becauseDiagramComponentaccesseswindowanddocument.
---
Preact Integration
Preact is compatible with Syncfusion React components since it mirrors the React API.
npm init preact
cd my-project
npm install @syncfusion/ej2-react-diagrams --saveCSS import — src/style.css
@import "../node_modules/@syncfusion/ej2-base/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/material3.css";
@import "../node_modules/@syncfusion/ej2-react-diagrams/styles/material3.css";Component — src/index.jsx
import { render } from 'preact';
import { DiagramComponent, Inject, UndoRedo } from '@syncfusion/ej2-react-diagrams';
export default function App() {
return (
<DiagramComponent id="diagram" width={'100%'} height={'400px'}>
<Inject services={[UndoRedo]} />
</DiagramComponent>
);
}
render(<App />, document.querySelector('#app'));---
Troubleshooting
Diagram renders blank / no shapes visible
- Ensure CSS is imported and the import path is correct (check
node_modules/prefix) - Confirm
idprop is set onDiagramComponent - Check that
nodesarray objects have validoffsetX/offsetYvalues
"Module not found" / feature not working
- Inject the required module via
<Inject services={[...]} /> - Verify the module is imported from
@syncfusion/ej2-react-diagrams
Next.js hydration error
- Add
'use client'at the top of the file containingDiagramComponent
TypeScript errors on `node.data`
- Cast to the appropriate interface:
(node.data as MyDataType).propertyName
Node IDs causing issues
- IDs must start with a letter, no spaces, no special characters (underscores are also disallowed)
- IDs must be unique across all nodes and connectors in the diagram
Related docs:
- Nodes → nodes.md
- Connectors → connectors.md
- Layouts → layouts.md
- Data Binding → data-binding.md
Groups and Containers in Syncfusion React Diagram
Table of Contents
- Groups vs Containers
- Creating Groups
- Group Runtime Operations
- Managing Group Children at Runtime
- Creating Containers
- Container Headers
- Container Padding
- Best Practices
- Troubleshooting
---
Groups vs Containers
| Feature | Group | Container |
|---|---|---|
| Type | Regular node with children[] | Node with shape.type = 'Container' |
| Visible boundary | No explicit border by default | Always has a visible bounding box |
| Header support | No built-in header | Optional title via shape.header |
| Child layout | Children positioned manually | Children use margin for internal layout |
| Auto-sizing | Grows to fit children | Grows to fit children + padding |
| Use case | Selecting/moving related nodes together | Labelled container boxes (e.g. UML packages, swimlane cells) |
Choose Groups when nodes need to move/resize together without a visible frame. Choose Containers when a visible boundary with an optional title is needed.
---
Creating Groups
A group is a node with a children array of child node IDs. Child nodes must be defined before the group in the nodes array.
Declarative Group Definition
import { useRef } from 'react';
import { DiagramComponent, NodeModel } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [
{
id: 'node1',
offsetX: 100, offsetY: 100,
width: 100, height: 80,
annotations: [{ content: 'Node 1' }],
style: { fill: '#6BA5D7', strokeColor: 'white' },
},
{
id: 'node2',
offsetX: 250, offsetY: 100,
width: 100, height: 80,
annotations: [{ content: 'Node 2' }],
style: { fill: '#357BD2', strokeColor: 'white' },
},
{
// Group node — must come AFTER its children in the array
id: 'group1',
children: ['node1', 'node2'], // IDs of children
style: { strokeWidth: 2, strokeColor: '#888' },
padding: { left: 10, right: 10, top: 10, bottom: 10 },
},
];
export default function App() {
const diagramRef = useRef<DiagramComponent>(null);
return (
<DiagramComponent
id="container"
ref={diagramRef}
width="100%" height="500px"
nodes={nodes}
/>
);
}Group with Connectors as Children
Connectors can also be part of a group, so they move with the group:
import { useRef } from 'react';
import { DiagramComponent, ConnectorModel, NodeModel } from '@syncfusion/ej2-react-diagrams';
const connectors: ConnectorModel[] = [
{ id: 'c1', type: 'Orthogonal', sourceID: 'node1', targetID: 'node2' },
];
const nodes: NodeModel[] = [
{ id: 'node1', offsetX: 100, offsetY: 100, width: 100, height: 80 },
{ id: 'node2', offsetX: 300, offsetY: 100, width: 100, height: 80 },
{
id: 'group1',
children: ['node1', 'node2', 'c1'], // connector included
style: { strokeWidth: 0 }, // hide group border
},
];
export default function App() {
const diagramRef = useRef<DiagramComponent>(null);
return (
<DiagramComponent
id="container"
ref={diagramRef}
width="100%" height="500px"
nodes={nodes}
connectors={connectors}
/>
);
}---
Group Runtime Operations
Group Selected Nodes
Programmatically group whatever is currently selected:
const diagramRef = useRef<DiagramComponent>(null);
function groupSelected() {
diagramRef.current?.group();
}
// Ensure nodes are selected first:
function selectAndGroup() {
const n1 = diagramRef.current?.getObject('node1') as NodeModel;
const n2 = diagramRef.current?.getObject('node2') as NodeModel;
diagramRef.current?.select([n1, n2]);
diagramRef.current?.group();
}Ungroup at Runtime
function ungroupSelected() {
// Select the group first, then ungroup
const grp = diagramRef.current?.getObject('group1') as NodeModel;
diagramRef.current?.select([grp]);
diagramRef.current?.unGroup();
}Add a Pre-defined Group at Runtime
const newGroup: NodeModel = {
id: 'group2',
children: ['node3', 'node4'],
};
diagramRef.current?.add(newGroup);Add Multiple Groups at Once
const elements: NodeModel[] = [
{ id: 'n3', offsetX: 400, offsetY: 100, width: 100, height: 80 },
{ id: 'n4', offsetX: 540, offsetY: 100, width: 100, height: 80 },
{ id: 'grp2', children: ['n3', 'n4'], padding: { left: 8, right: 8, top: 8, bottom: 8 } },
];
diagramRef.current?.addElements(elements);ThecollectionChangeevent fires for each element added viaaddElements.
---
Managing Group Children at Runtime
Add a Child to an Existing Group
const group = diagramRef.current?.getObject('group1') as NodeModel;
const child = diagramRef.current?.getObject('node5') as NodeModel;
diagramRef.current?.addChildToGroup(group, child);Remove a Child from a Group
const group = diagramRef.current?.getObject('group1') as NodeModel;
const child = diagramRef.current?.getObject('node2') as NodeModel;
diagramRef.current?.removeChildFromGroup(group, child);After removing, the child node remains on the canvas as an independent node.
---
Creating Containers
Containers use shape.type = 'Container' and list children by ID. Child nodes use margin to position themselves inside the container.
const nodes: NodeModel[] = [
{
id: 'child1',
margin: { left: 40, top: 30 }, // position relative to container top-left
width: 110, height: 60,
style: { fill: '#357BD2', strokeColor: 'white' },
annotations: [{ content: 'Component A', style: { color: 'white' } }],
},
{
id: 'child2',
margin: { left: 200, top: 100 },
width: 110, height: 60,
style: { fill: '#6BA5D7', strokeColor: 'white' },
annotations: [{ content: 'Component B', style: { color: 'white' } }],
},
{
id: 'container1',
width: 380, height: 220,
offsetX: 250, offsetY: 200,
shape: {
type: 'Container',
children: ['child1', 'child2'],
},
style: {
fill: '#E9EEFF',
strokeColor: '#2546BB',
strokeWidth: 1,
},
},
];---
Container Headers
Add a visible title bar to a container using shape.header:
{
id: 'container1',
width: 380, height: 250,
offsetX: 250, offsetY: 200,
shape: {
type: 'Container',
header: {
annotation: {
content: 'Authentication Module',
style: { fontSize: 14, bold: true, color: 'white' },
},
height: 40,
style: { fill: '#3c63ac', strokeColor: '#30518f' },
},
children: ['child1', 'child2'],
},
style: { fill: 'white', strokeColor: '#30518f', strokeDashArray: '4 4' },
}| Header Property | Type | Description |
|---|---|---|
annotation.content | string | Title text displayed in the header |
annotation.style | TextStyleModel | Font size, bold, color, italic |
height | number | Header bar height in pixels |
style.fill | string | Header background color |
style.strokeColor | string | Header border color |
Double-click the header area to edit the title inline.
---
Container Padding
Use the padding property on either groups or containers to add spacing between the boundary and children:
{
id: 'group1',
children: ['node1', 'node2'],
padding: { left: 15, right: 15, top: 15, bottom: 15 },
}For containers, padding also affects where the auto-resize boundary stops — ensuring children have breathing room within the container frame.
---
Best Practices
- Child declaration order: Always define child nodes/connectors before the group or container node in the
nodesarray. - Use `padding` on groups/containers to prevent children from sitting flush against the boundary.
- Use `margin` for container children (not
offsetX/offsetY) — container children are positioned relative to the container, not the canvas. - Avoid nesting too deeply: Group-inside-group-inside-container creates complex selection/move behaviors. Keep nesting to 2 levels maximum.
- Style group borders: By default, a group has a visible dashed border. Set
style: { strokeWidth: 0 }to make it invisible if you only need logical grouping.
---
Troubleshooting
Children not appearing inside the group/container → Ensure child node IDs in children[] match existing node IDs exactly (case-sensitive). → Declare children before the group in the nodes array.
Container children misplaced after drag → Container children use margin for positioning — do not set offsetX/offsetY directly on children of a Container node.
Group does not auto-resize around children → Groups do resize to fit children. If children appear outside, verify that the children's offsetX/offsetY values are realistic relative to the group.
`addChildToGroup` has no effect → Retrieve the group via diagram.getObject(id) before calling the method. The group reference must be a live object from the diagram model.
Ungrouping does not remove the group node → unGroup dissolves the group and removes the group container — children remain on the canvas individually. If the group appears to persist, check for a second nested group.
Interaction and Tools
Table of Contents
- Overview
- Selection
- Drag and Resize
- Tool Modes
- Drawing Tools
- Constraints
- Commands
- Undo and Redo
- Context Menu
- User Handles
- Fixed User Handles
- Best Practices
- Troubleshooting
---
Overview
The Syncfusion React Diagram component provides a rich set of interaction capabilities. Users can select, drag, resize, rotate, and connect diagram elements through mouse and keyboard. The tool property on DiagramComponent controls which interaction modes are active. Constraints (bitwise flags) enable/disable specific behaviors per element or diagram-wide.
Required module injections for specific features:
import { DiagramComponent, Inject, UndoRedo, DiagramContextMenu, ConnectorEditing } from '@syncfusion/ej2-react-diagrams';
<DiagramComponent ...>
<Inject services={[UndoRedo, DiagramContextMenu, ConnectorEditing]} />
</DiagramComponent>---
Selection
Single and Multiple Selection
Click an element to select it — previous selections are cleared. Hold Ctrl and click to add to the selection. Drag a rubber-band rectangle to select multiple elements.
import { DiagramComponent, NodeModel } from '@syncfusion/ej2-react-diagrams';
let diagramInstance: DiagramComponent;
const nodes: NodeModel[] = [
{ id: 'n1', offsetX: 100, offsetY: 100, width: 80, height: 60 },
{ id: 'n2', offsetX: 250, offsetY: 100, width: 80, height: 60 },
];
function App() {
return (
<DiagramComponent
id="container"
ref={(diagram) => (diagramInstance = diagram)}
width="100%"
height="600px"
nodes={nodes}
created={() => {
// Programmatic single selection
diagramInstance.select([diagramInstance.nodes[0]]);
// Select all
diagramInstance.selectAll();
// Clear selection
diagramInstance.clearSelection();
}}
/>
);
}Rubber-Band Selection Mode
Control whether rubber-band selection requires elements to be fully or partially inside the rectangle:
<DiagramComponent
id="container"
width="100%"
height="600px"
// CompleteIntersect | PartialIntersect
selectedItems={{ rubberBandSelectionMode: 'CompleteIntersect' }}
/>Toggle Selection
Allow click-to-deselect behavior:
<DiagramComponent
id="container"
width="100%"
height="600px"
nodes={nodes}
selectedItems={{ canToggleSelection: true }}
/>Get Selected Items
const selectedNodes = diagramInstance.selectedItems.nodes;
const selectedConnectors = diagramInstance.selectedItems.connectors;Selection Events
| Event | Trigger |
|---|---|
selectionChange | When selection changes |
click | When an element is clicked |
---
Drag and Resize
Drag
Click and drag any selected element to move it. Dragging one element in a multi-selection moves all selected elements together.
Event: positionChange fires while dragging.
Resize
Eight resize thumbs appear around the selector. Drag a corner thumb to resize; the opposite corner stays fixed.
Event: sizeChange fires while resizing.
Maintain Aspect Ratio
import { NodeConstraints } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [{
id: 'n1',
offsetX: 250, offsetY: 250,
width: 100, height: 100,
constraints: NodeConstraints.Default | NodeConstraints.AspectRatio,
}];Customize Resize Thumb Size
<DiagramComponent
id="container"
width="100%"
height="600px"
nodes={nodes}
selectedItems={{ handleSize: 20 }}
/>Rotation
A rotate handle appears above the selector. Drag it in a circular direction to rotate the node around its pivot point (default: center {x: 0.5, y: 0.5}).
Restrict Drag/Drop in Negative Axis
Prevent elements from being dragged into negative coordinate areas:
import { DiagramConstraints } from '@syncfusion/ej2-react-diagrams';
<DiagramComponent
id="container"
width="100%"
height="600px"
constraints={DiagramConstraints.Default | DiagramConstraints.RestrictNegativeAxisDragDrop}
/>---
Tool Modes
Set the active interaction mode with the tool property using DiagramTools:
| Tool | Behavior |
|---|---|
DiagramTools.Default | Select + multi-select |
DiagramTools.SingleSelect | Single element selection only |
DiagramTools.MultipleSelect | Multi-select via rubber-band |
DiagramTools.ZoomPan | Pan the diagram view |
DiagramTools.DrawOnce | Draw one element then return to select mode |
DiagramTools.ContinuousDraw | Keep drawing until deactivated |
DiagramTools.None | Disable all interaction |
Precedence (highest → lowest): ContinuousDraw → DrawOnce → ZoomPan → MultipleSelect → SingleSelect → None
import { DiagramTools } from '@syncfusion/ej2-react-diagrams';
// Pan only
<DiagramComponent tool={DiagramTools.ZoomPan} ... />
// Allow panning while still enabling selection
<DiagramComponent tool={DiagramTools.SingleSelect | DiagramTools.ZoomPan} ... />---
Drawing Tools
Use drawing tools to create nodes and connectors interactively at runtime.
Draw a Shape Node
import { DiagramTools, BasicShapeModel, NodeModel } from '@syncfusion/ej2-react-diagrams';
let diagramInstance: DiagramComponent;
function App() {
return (
<DiagramComponent
id="container"
ref={(diagram) => (diagramInstance = diagram)}
width="100%"
height="600px"
created={() => {
const node: BasicShapeModel = { type: 'Basic', shape: 'Rectangle' };
diagramInstance.drawingObject = { shape: node } as NodeModel;
diagramInstance.tool = DiagramTools.ContinuousDraw;
diagramInstance.dataBind();
}}
/>
);
}Draw a Connector
import { ConnectorModel, DiagramTools } from '@syncfusion/ej2-react-diagrams';
created={() => {
const connector: ConnectorModel = { id: 'c1', type: 'Orthogonal' };
diagramInstance.drawingObject = connector;
diagramInstance.tool = DiagramTools.DrawOnce;
diagramInstance.dataBind();
}}Draw Polygon / Polyline / Freehand
// Polygon node
diagramInstance.drawingObject = { shape: { type: 'Basic', shape: 'Polygon' } };
diagramInstance.tool = DiagramTools.DrawOnce;
// Polyline connector
diagramInstance.drawingObject = { id: 'c1', type: 'Polyline' } as ConnectorModel;
// Freehand connector
diagramInstance.drawingObject = { id: 'c1', type: 'Freehand' } as ConnectorModel;Note: To edit polyline/freehand segment thumbs, injectConnectorEditingand enableConnectorConstraints.DragSegmentThumb.
Element Draw Event
import { IElementDrawEventArgs } from '@syncfusion/ej2-react-diagrams';
<DiagramComponent
...
elementDraw={(args: IElementDrawEventArgs) => {
if (args.state === 'Completed') {
console.log('Element drawn:', args.element);
}
}}
/>---
Constraints
Constraints are bitwise flags. Use | to enable and & ~ to disable specific behaviors.
Diagram Constraints
import { DiagramConstraints } from '@syncfusion/ej2-react-diagrams';
// Disable page editing
<DiagramComponent
constraints={DiagramConstraints.Default & ~DiagramConstraints.PageEditable}
/>
// Enable connector bridging
<DiagramComponent
constraints={DiagramConstraints.Default | DiagramConstraints.Bridging}
/>
// Disable zoom + page editing together
<DiagramComponent
constraints={DiagramConstraints.Default & ~(DiagramConstraints.PageEditable | DiagramConstraints.Zoom)}
/>Key DiagramConstraints values:
| Value | Effect |
|---|---|
Zoom | Enable/disable zoom |
PanX / PanY | Horizontal / vertical pan |
Undo/redo | History tracking |
PageEditable | Allow content editing |
Bridging | Connector bridge overlaps |
Virtualization | Large diagram performance |
LineRouting | Auto-route connectors |
Node Constraints
import { NodeConstraints } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [{
id: 'n1',
offsetX: 200, offsetY: 200,
// Disable rotate + resize
constraints: NodeConstraints.Default & ~(NodeConstraints.Rotate | NodeConstraints.Resize),
}];
// Enable shadow
constraints: NodeConstraints.Default | NodeConstraints.Shadow
// Enable tooltip (also set tooltip property)
constraints: NodeConstraints.Default | NodeConstraints.TooltipConnector Constraints
import { ConnectorConstraints } from '@syncfusion/ej2-react-diagrams';
const connectors: ConnectorModel[] = [{
id: 'c1', type: 'Orthogonal',
sourcePoint: { x: 100, y: 100 }, targetPoint: { x: 300, y: 200 },
// Lock endpoints
constraints: ConnectorConstraints.Default & ~(ConnectorConstraints.DragSourceEnd | ConnectorConstraints.DragTargetEnd),
}];Selector Constraints
Control which resize/rotate handles appear on selection:
import { SelectorConstraints } from '@syncfusion/ej2-react-diagrams';
// Hide rotation handle
<DiagramComponent
selectedItems={{ constraints: SelectorConstraints.All & ~SelectorConstraints.Rotate }}
/>
// Hide all resize handles
<DiagramComponent
selectedItems={{ constraints: SelectorConstraints.All & ~SelectorConstraints.ResizeAll }}
/>Boundary Constraints
Limit drag/drop to the page area:
<DiagramComponent
pageSettings={{ boundaryConstraints: 'Page', width: 800, height: 600 }}
/>---
Commands
Diagram provides programmatic commands for alignment, sizing, clipboard, grouping, ordering, zoom, and nudge.
Alignment
// Align selected nodes to the left of the selection boundary
diagramInstance.align('Left', diagramInstance.selectedItems.nodes, 'Selector');
// Other options: 'Right' | 'Center' | 'Top' | 'Bottom' | 'Middle'Distribution
// Distribute nodes with equal spacing (right-to-left)
diagramInstance.distribute('RightToLeft', diagramInstance.selectedItems.nodes);Sizing
// Make all selected nodes the same width as the first node
diagramInstance.sameSize('Width', diagramInstance.nodes);
// Options: 'Width' | 'Height' | 'Size'Clipboard
diagramInstance.cut(); // Cut selected
diagramInstance.copy(); // Copy selected
diagramInstance.paste(); // Paste clipboard
// Paste specific nodes at defined positions
diagramInstance.paste([{ id: 'n1', offsetX: 400, offsetY: 100, width: 100 }]);Keyboard shortcuts: Ctrl+X, Ctrl+C, Ctrl+V
Grouping
diagramInstance.group(); // Ctrl+G
diagramInstance.unGroup(); // Ctrl+Shift+UZ-Order
diagramInstance.bringToFront(); // Ctrl+Shift+F
diagramInstance.sendToBack(); // Ctrl+Shift+B
diagramInstance.moveForward(); // Ctrl+]
diagramInstance.sendBackward(); // Ctrl+[Zoom and Fit
// Zoom programmatically
diagramInstance.zoomTo({ type: 'ZoomIn', zoomFactor: 0.2, focusPoint: { x: 0.5, y: 0.5 } });
diagramInstance.zoomTo({ type: 'ZoomOut', zoomFactor: 0.2 });
// Fit entire diagram into view
diagramInstance.fitToPage({ mode: 'Page', region: 'Content', canZoomIn: false });
diagramInstance.fitToPage({ mode: 'Width', region: 'Content', canZoomIn: false });Nudge
// Move selected elements 1px in a direction
diagramInstance.nudge('Up');
diagramInstance.nudge('Down');
diagramInstance.nudge('Left');
diagramInstance.nudge('Right');Arrow keys move by 1px; Shift+Arrow moves by 5px.
Bring Into View / Bring to Center
import { Rect } from '@syncfusion/ej2-react-diagrams';
const bounds = diagramInstance.nodes[0].wrapper.bounds;
const rect = new Rect(bounds.x, bounds.y, bounds.width, bounds.height);
diagramInstance.bringIntoView(rect); // Scroll node into viewport
diagramInstance.bringToCenter(rect); // Center node in viewportCustom Command Manager
Bind custom logic to keyboard shortcuts:
import { CommandManager, Keys, KeyModifiers } from '@syncfusion/ej2-react-diagrams';
const commandManager: CommandManager = {
commands: [{
name: 'customClone',
canExecute: () => diagramInstance.selectedItems.nodes.length > 0,
execute: () => { diagramInstance.copy(); diagramInstance.paste(); },
gesture: { key: Keys.G, keyModifiers: KeyModifiers.None },
}],
};
<DiagramComponent commandManager={commandManager} ... />Built-in Keyboard Shortcuts Reference
| Shortcut | Action |
|---|---|
| Ctrl+A | Select all |
| Ctrl+Z / Ctrl+Y | Undo / Redo |
| Ctrl+C / Ctrl+V / Ctrl+X | Copy / Paste / Cut |
| Ctrl+G / Ctrl+Shift+U | Group / Ungroup |
| Delete | Delete selected |
| F2 | Start label edit |
| Esc | Stop label edit |
| Ctrl+R / Ctrl+L | Rotate CW / CCW |
| Ctrl+H / Ctrl+J | Flip horizontal / vertical |
| Ctrl++ / Ctrl+- | Zoom in / out |
---
Undo and Redo
Requires the UndoRedo module to be injected.
Basic Usage
import { UndoRedo } from '@syncfusion/ej2-react-diagrams';
<DiagramComponent ...>
<Inject services={[UndoRedo]} />
</DiagramComponent>
// Programmatic undo/redo
diagramInstance.undo(); // Ctrl+Z
diagramInstance.redo(); // Ctrl+YGroup Multiple Actions into One Undo Step
diagramInstance.startGroupAction();
// Make several changes
diagramInstance.nodes[0].style.fill = 'red';
diagramInstance.nodes[1].style.fill = 'blue';
diagramInstance.dataBind();
diagramInstance.endGroupAction();
// Now a single Ctrl+Z undoes both changes togetherLimit History Stack Size
<DiagramComponent
historyManager={{ stackLimit: 20 }}
...
>
<Inject services={[UndoRedo]} />
</DiagramComponent>Prevent Specific Actions from Being Recorded
diagramInstance.historyManager.canLog = (entry) => {
// Returning entry.cancel = true prevents the entry from being logged
if (entry.type === 'PositionChanged') {
entry.cancel = true;
}
return entry;
};Check Undo/Redo Availability
const canUndo = diagramInstance.historyManager.canUndo;
const canRedo = diagramInstance.historyManager.canRedo;
// Clear all history
diagramInstance.clearHistory();
// Retrieve stacks
const undoStack = diagramInstance.getHistoryStack(true); // undo
const redoStack = diagramInstance.getHistoryStack(false); // redoHistory Change Event
import { IHistoryChangeArgs } from '@syncfusion/ej2-react-diagrams';
<DiagramComponent
historyChange={(args: IHistoryChangeArgs) => {
console.log('History changed:', args.change);
}}
...
>
<Inject services={[UndoRedo]} />
</DiagramComponent>---
Context Menu
Requires the DiagramContextMenu module. Also add @syncfusion/ej2-navigations CSS to your styles.
Enable Default Context Menu
import { DiagramContextMenu, Inject } from '@syncfusion/ej2-react-diagrams';
<DiagramComponent
contextMenuSettings={{ show: true }}
...
>
<Inject services={[DiagramContextMenu]} />
</DiagramComponent>Custom Context Menu Items
import { useRef } from 'react';
import {DiagramComponent, DiagramContextMenu, DiagramMenuEventArgs, Inject, NodeModel } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [
{
id: 'node1',
offsetX: 100, offsetY: 200,
width: 60, height: 60,
},
];
export default function App() {
const diagramRef = useRef<DiagramComponent>(null);
return (
<DiagramComponent
ref={diagramRef}
nodes={nodes}
width={1000}
height={500}
contextMenuSettings={{
show: true,
showCustomMenuOnly: true, // hide default items
items: [
{
text: 'Fill Color',
id: 'fill',
target: '.e-elementcontent',
iconCss: 'e-icons e-paint-bucket',
items: [
{ id: 'red', text: 'Red' },
{ id: 'blue', text: 'Blue' },
],
},
{ text: 'Clone', id: 'clone', target: '.e-elementcontent', iconCss: 'e-icons e-copy' },
],
}}
contextMenuClick={(args: DiagramMenuEventArgs) => {
const diagram = diagramRef.current;
if (!diagram) return;
const node = diagram.selectedItems.nodes![0];
if (!node) return;
if (args.item.text === 'Red') { node.style!.fill = 'red'; diagram.dataBind(); }
if (args.item.text === 'Blue') { node.style!.fill = 'blue'; diagram.dataBind(); }
if (args.item.id === 'clone') { diagram.copy(); diagram.paste(); }
}}
>
<Inject services={[DiagramContextMenu]} />
</DiagramComponent>
);
}Conditionally Hide Menu Items
import { DiagramBeforeMenuOpenEventArgs } from '@syncfusion/ej2-react-diagrams';
contextMenuOpen={(args: DiagramBeforeMenuOpenEventArgs) => {
const node = diagramInstance.selectedItems.nodes[0];
const connector = diagramInstance.selectedItems.connectors[0];
if (node || connector) {
args.hiddenItems = ['selectAll'];
} else {
args.hiddenItems = ['applyFill', 'applyStroke'];
}
}}Context Menu Events
| Event | Description |
|---|---|
contextMenuBeforeItemRender | Fires before each menu item renders (use for templates) |
contextMenuOpen | Fires when menu opens (use to hide/show items) |
contextMenuClick | Fires when a menu item is clicked |
---
User Handles
User handles are icons that appear around a selected element for quick actions.
Define User Handles
import { useRef } from 'react';
import { DiagramComponent, NodeModel, UserHandleModel } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [
{
id: 'node1',
offsetX: 100, offsetY: 200,
width: 60, height: 60,
},
];
const userHandles: UserHandleModel[] = [{
name: 'clone',
pathData: 'M0 0 L100 0 L100 80 L0 80 Z', // SVG path
offset: 1,
side: 'Bottom',
horizontalAlignment: 'Left',
verticalAlignment: 'Bottom',
margin: { left: 5, bottom: 5 },
tooltip: { content: 'Clone Node' },
// Appearance
size: 30,
borderWidth: 2,
borderColor: '#6BA5D7',
backgroundColor: '#fff',
pathColor: '#6BA5D7',
visible: true,
}];
export default function App() {
const diagramRef = useRef<DiagramComponent>(null);
return (
<DiagramComponent
selectedItems={{ userHandles }}
ref={diagramRef}
width={1000}
height={500}
nodes={nodes}
onUserHandleMouseDown={(args) => {
const diagram = diagramRef.current;
if (args.element.name === 'clone') {
diagram.copy();
diagram.paste();
}
}}
/>
);
}
Alignment Options
offset | side | Position |
|---|---|---|
| 0 | Left | Top-left corner |
| 1 | Bottom | Bottom-right area |
| 0.5 | Top | Top-center |
Selectively Disable for Nodes or Connectors
const userHandles: UserHandleModel[] = [
{ name: 'nodeOnly', ..., disableConnectors: true },
{ name: 'connectorOnly', ..., disableNodes: true },
{ name: 'both', ... },
];User Handle Events
| Event | Description |
|---|---|
onUserHandleMouseDown | Mouse button pressed on handle |
onUserHandleMouseUp | Mouse button released on handle |
onUserHandleMouseEnter | Mouse enters handle region |
onUserHandleMouseLeave | Mouse leaves handle region |
click | Handle clicked |
---
Fixed User Handles
Fixed user handles are permanently visible on specific nodes or connectors regardless of selection state.
import { useRef } from 'react';
import { DiagramComponent, NodeModel } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [{
id: 'n1',
offsetX: 250, offsetY: 250,
width: 100, height: 100,
fixedUserHandles: [{
id: 'colorHandle',
pathData: 'M0 0 L100 0 L100 80 L0 80 Z',
width: 20, height: 20,
offset: { x: 1, y: 0 }, // top-right corner
margin: { left: 20 },
fill: 'white',
handleStrokeColor: '#6BA5D7',
iconStrokeColor: '#6BA5D7',
cornerRadius: 4,
tooltip: { content: 'Change color' },
}],
}];
export default function App() {
const diagramRef = useRef<DiagramComponent>(null);
return (
<DiagramComponent
nodes={nodes}
ref={diagramRef}
width={1000}
height={500}
fixedUserHandleClick={(args) => {
const diagram = diagramRef.current;
if (args.fixedUserHandle.id === 'colorHandle') {
args.element.style.fill = '#6BA5D7';
diagram.dataBind();
}
}}
/>
);
}Tip: Each fixedUserHandle id must be unique across the diagram.---
Best Practices
- Inject only needed modules — each service (UndoRedo, DiagramContextMenu, ConnectorEditing) adds overhead; inject only what your diagram uses.
- Use `startGroupAction` / `endGroupAction` when making batch programmatic changes so users can undo them in a single step.
- Set `stackLimit` to cap history size in applications with heavy editing to prevent memory growth.
- Use `showCustomMenuOnly: true` when implementing a fully custom context menu to avoid conflicts with default items.
- Set `disableNodes` / `disableConnectors` on user handles to avoid showing irrelevant actions for an element type.
- Apply constraints at the element level (node/connector) rather than globally when possible to preserve selective interactivity.
- Use `boundaryConstraints: 'Page'` to prevent users from accidentally moving content outside the visible area.
---
Troubleshooting
Undo/Redo not working:
- Ensure
UndoRedois injected via<Inject services={[UndoRedo]} />. - Check that
DiagramConstraints.Defaultis set (it includes undo/redo by default); verify it hasn't been removed.
Context menu not appearing:
- Inject
DiagramContextMenuand setcontextMenuSettings={{ show: true }}. - Add the navigations CSS import:
@import "~@syncfusion/ej2-navigations/styles/material.css".
User handles not visible:
- Define handles in
selectedItems.userHandles— they only show when an element is selected. - For always-visible handles, use
fixedUserHandleson the node/connector definition.
Drawing tool reverts to select after one draw:
DiagramTools.DrawOnceis intended for single draws. UseDiagramTools.ContinuousDrawto keep drawing.
Resize handles not showing:
- Check
SelectorConstraints—SelectorConstraints.ResizeAllmust be included. - Verify node-level
NodeConstraints.Resizeis not disabled.
Cannot connect to a port:
- Check
PortConstraints.InConnect/PortConstraints.OutConnectare enabled on the port. - Ensure
NodeConstraints.InConnect/NodeConstraints.OutConnectare set on the target/source node.
Labels and Annotations in Syncfusion React Diagram
Table of Contents
- Annotation Fundamentals
- Creating Annotations
- Node Annotation Positioning
- Connector Annotation Positioning
- Annotation Style
- Text Wrapping and Overflow
- Templates
- Hyperlinks
- Interactive Annotations
- Annotation Events
- Runtime Operations
- Advanced Properties
- Troubleshooting
---
Annotation Fundamentals
Annotations are text blocks displayed over nodes and connectors. Both nodes and connectors accept multiple annotations via an annotations array. Each annotation has independent style, position, and interaction settings.
ID rules: Annotation IDs must start with a letter. No spaces, underscores, or special characters.
---
Creating Annotations
Node annotation
import { DiagramComponent, NodeModel } from '@syncfusion/ej2-react-diagrams';
const nodes: NodeModel[] = [
{
id: 'n1',
offsetX: 250, offsetY: 250,
width: 120, height: 60,
annotations: [{ content: 'Decision' }] // defaults to center
}
];Connector annotation
import { ConnectorModel } from '@syncfusion/ej2-react-diagrams';
const connectors: ConnectorModel[] = [
{
sourcePoint: { x: 100, y: 100 },
targetPoint: { x: 300, y: 300 },
type: 'Orthogonal',
annotations: [{ content: 'Yes', offset: 0.5 }] // 0–1 along the connector path
}
];Multiple annotations per element
annotations: [
{ content: 'Left', offset: { x: 0.12, y: 0.1 } },
{ content: 'Center', offset: { x: 0.5, y: 0.5 } },
{ content: 'Right', offset: { x: 0.82, y: 0.9 } }
]---
Node Annotation Positioning
Node annotations use a two-step positioning system: 1. `offset` — fractional {x, y} within the node boundary; (0,0) = top-left, (1,1) = bottom-right, default (0.5, 0.5) = center 2. `horizontalAlignment` / `verticalAlignment` — how the annotation box aligns at the computed offset point
Common positions
| Position | offset | Notes |
|---|---|---|
| Center | { x: 0.5, y: 0.5 } | Default |
| Top center | { x: 0.5, y: 0 } | + verticalAlignment: 'Bottom' to appear outside above |
| Bottom center | { x: 0.5, y: 1 } | + verticalAlignment: 'Top' to appear outside below |
| Left center | { x: 0, y: 0.5 } | + horizontalAlignment: 'Right' to appear outside left |
| Right center | { x: 1, y: 0.5 } | + horizontalAlignment: 'Left' to appear outside right |
annotations: [{
content: 'External label',
offset: { x: 0.5, y: 1 },
verticalAlignment: 'Top', // places label below the node
margin: { top: 8 } // adds extra spacing
}]Fixed size annotations
annotations: [{
content: 'Long annotation content',
offset: { x: 0, y: 1 },
width: 120,
height: 50
}]Update positioning at runtime
diagramRef.current!.nodes[0].annotations[0].offset = { x: 0, y: 0.5 };
diagramRef.current!.nodes[0].annotations[0].horizontalAlignment = 'Right';
diagramRef.current!.dataBind();---
Connector Annotation Positioning
Connector annotations use a numeric offset (0–1) along the path from source to target:
offset | Position |
|---|---|
0 | At source point |
0.5 | Midpoint (default) |
1 | At target point |
Alignment options
annotations: [
{ content: 'Start', offset: 0, alignment: 'Before' }, // before midpoint
{ content: 'End', offset: 1, alignment: 'After' } // after midpoint
]Displacement (shift perpendicular to path)
annotations: [{
content: 'Label',
alignment: 'After',
displacement: { x: 50, y: 50 } // only valid with 'Before' or 'After'
}]Rotate annotation with connector
annotations: [{ content: 'Flow label', segmentAngle: true, offset: 0.3 }]Drag limit (constrain label dragging on connector)
import { AnnotationConstraints } from '@syncfusion/ej2-react-diagrams';
annotations: [{
content: 'Draggable label',
constraints: AnnotationConstraints.Interaction | AnnotationConstraints.Drag,
dragLimit: { left: 20, right: 20, top: 10, bottom: 10 }
}]---
Annotation Style
Basic text styling
annotations: [{
content: 'Styled Label',
style: {
color: '#1565C0', // text color
fontSize: 13,
fontFamily: 'Segoe UI',
bold: true,
italic: false,
textDecoration: 'None', // 'Underline' | 'LineThrough' | 'Overline' | 'None'
textAlign: 'Center', // 'Left' | 'Center' | 'Right' | 'Justify'
fill: 'transparent', // background fill of annotation box
strokeColor: 'transparent',
opacity: 1
}
}]Rotation
annotations: [{ content: 'Rotated', rotateAngle: 45 }]Rotation reference
Controls whether the annotation rotates with its parent node or stays fixed on the page:
annotations: [{ content: 'Fixed', rotationReference: 'Page' }] // 'Page' | 'Parent'---
Text Wrapping and Overflow
Text wrapping
textWrapping | Behavior |
|---|---|
'WrapWithOverflow' | Wraps; very long words may overflow (default) |
'Wrap' | Wraps strictly within bounds |
'NoWrap' | Single line; truncates if too wide |
Text overflow
textOverflow | Behavior |
|---|---|
'Wrap' | Shows all text with vertical overflow (default) |
'Clip' | Clips at boundary |
'Ellipsis' | Shows ... when truncated |
annotations: [{
content: 'A very long annotation that wraps',
style: {
textWrapping: 'Wrap',
textOverflow: 'Ellipsis'
}
}]---
Templates
String template (HTML/SVG inline)
annotations: [{
template: '<div><input type="button" value="Submit" /></div>',
width: 100,
height: 40
}]Always specifywidthandheightwhen using templates.
Functional template
function annotationTemplate(props: any) {
return (
<div style={{ width: '100px', overflow: 'hidden' }}>
<input type="button" value={props.id} />
</div>
);
}
<DiagramComponent
annotationTemplate={annotationTemplate.bind(this)}
...
/>---
Hyperlinks
import { DiagramComponent } from '@syncfusion/ej2-react-diagrams';
// Node annotation hyperlink
annotations: [{
hyperlink: {
link: 'https://example.com',
content: 'Visit Site', // display text
color: '#1565C0',
hyperlinkOpenState: 'NewWindow', // 'NewWindow' | 'NewTab' | 'CurrentPage'
textDecoration: 'Underline'
}
}]---
Interactive Annotations
Enable interaction
Labels are static by default. Enable dragging, resizing, and rotating with AnnotationConstraints.Interaction:
import { AnnotationConstraints } from '@syncfusion/ej2-react-diagrams';
annotations: [{
content: 'Draggable',
constraints: AnnotationConstraints.Interaction
}]Read-only annotation
annotations: [{
content: 'Cannot edit',
constraints: AnnotationConstraints.ReadOnly
}]Programmatic editing
diagramRef.current!.startTextEdit(diagramRef.current!.nodes[0]);Interactive editing: double-click on a label, or select it and press F2.
---
Annotation Events
| Event | Trigger |
|---|---|
doubleClick | User double-clicks — opens edit mode |
textEdit | Edit session ends (focus lost) |
keyDown | Key pressed while annotation focused |
keyUp | Key released while annotation focused |
selectionChange | Annotation selected/deselected |
Text edit event
<DiagramComponent
textEdit={(args) => {
console.log('Old text:', args.oldValue);
console.log('New text:', args.newValue);
// Revert if empty
if (args.newValue.trim() === '') {
args.cancel = true;
}
}}
/>Prevent edit on double-click
<DiagramComponent
doubleClick={(args) => {
args.cancel = true; // blocks editing
}}
/>---
Runtime Operations
Add annotation
import { ShapeAnnotationModel } from '@syncfusion/ej2-react-diagrams';
const annotation: ShapeAnnotationModel[] = [{
id: 'label2',
content: 'New Annotation'
}];
diagramRef.current!.addLabels(diagramRef.current!.nodes[0], annotation);
diagramRef.current!.dataBind();Update annotation
diagramRef.current!.nodes[0].annotations[0].content = 'Updated text';
diagramRef.current!.dataBind();Remove annotation
const node = diagramRef.current!.nodes[0];
diagramRef.current!.removeLabels(node, node.annotations);---
Advanced Properties
| Property | Type | Description |
|---|---|---|
content | string | Annotation text |
offset | PointModel (node) / number (connector) | Position |
horizontalAlignment | `'Left' \ | 'Center' \ |
verticalAlignment | `'Top' \ | 'Center' \ |
margin | MarginModel | Spacing around the annotation |
style | TextStyleModel | Font, color, wrapping, alignment |
rotateAngle | number | Rotation degrees |
rotationReference | `'Page' \ | 'Parent'` |
visibility | boolean | Show/hide annotation |
constraints | AnnotationConstraints | Interaction flags |
dragLimit | MarginModel | Drag boundary for connector labels |
template | string | HTML/SVG inline template |
hyperlink | HyperlinkModel | Clickable link inside label |
alignment | `'Before' \ | 'Center' \ |
displacement | PointModel | Perpendicular offset from path |
segmentAngle | boolean | Rotate annotation with connector angle |
---
Troubleshooting
Annotation not visible
- Check
visibilityis notfalse - For connectors, verify
offsetis between 0 and 1
Text cut off / overflowing
- Set explicit
widthandheighton the annotation - Adjust
textWrappingto'Wrap'andtextOverflowto'Clip'or'Ellipsis'
Annotation doesn't move after `offset` change
- Must call
diagramRef.current!.dataBind()after every runtime change
Template not rendering correctly
- Always specify
widthandheighton the annotation when usingtemplate
Label editing with F2 not working
- Ensure
AnnotationConstraints.ReadOnlyis NOT set on that annotation
Related docs:
- Nodes → nodes.md
- Connectors → connectors.md
Related skills
How it compares
Pick syncfusion-react-diagram for licensed Syncfusion canvas UIs; pick generic React Flow skills for open-source node editors without Syncfusion.
FAQ
What does syncfusion-react-diagram teach agents?
The syncfusion-react-diagram skill teaches agents to implement Syncfusion React Diagram with correct setup, shapes, connectors, data binding, and automatic layouts. It uses SKILL.md and references/ guides from the react-ui-components-skills pack.
How do you install syncfusion-react-diagram?
Developers install syncfusion-react-diagram by running npx skills add syncfusion/react-ui-components-skills in the project. Agents then read the Diagram SKILL.md to generate grounded React code.