Migrating from mxGraph to Syncfusion® JavaScript Diagram control

11 Jun 202524 minutes to read

mxGraph was once one of the most popular client-side diagramming libraries for JavaScript. However, with its official end-of-life status since 2020, the need for modern alternatives has grown. Syncfusion® JavaScript Diagram presents a powerful and feature-rich option with robust features, better performance, and extensive documentation. This guide will walk you through setting up the Syncfusion® JavaScript Diagram, and migrating typical diagramming scenarios from mxGraph to this platform. You’ll also learn how to convert existing mxGraph diagrams into an EJ2-compatible format and continue building with a supported solution.

Why Switch to Syncfusion® JavaScript Diagram from mxGraph

  • Enhanced Performance: Syncfusion® JavaScript Diagram offers superior performance for handling complex and large diagrams effectively.
  • Active Maintenance: Syncfusion® JavaScript Diagram is regularly updated and supported, unlike mxGraph, which is no longer maintained.
  • Modern Integration: Seamlessly integrates with frameworks like Angular, React, and Vue.
  • Rich Customization: Provides advanced customization options with a wide range of shapes and connectors.
  • Robust Support: Backed by strong community support and extensive documentation.

Migrating mxGraph XML data to Syncfusion® JavaScript Diagram

Let’s say you have diagram data stored as mxGraph XML format. To migrate this data to Syncfusion® JavaScript Diagram, you need to convert the XML by following several key steps to achieve visual consistency with the original designs:

  1. Load and Parse XML: Fetch the mxGraph XML data and use DOMParser to convert it into a document format for further processing.

  2. Extract Diagram Elements: Identify and extract nodes and connectors. Additionally, gather annotations, shapes, and styles to ensure all visual aspects are captured.

  3. Convert to Syncfusion® JavaScript Diagram Format: Transform the extracted nodes, connectors, annotations, and styles into the format required by Syncfusion® JavaScript Diagram.

  4. Render the Diagram: Use the Syncfusion® JavaScript Diagram API to create and render the diagram with the converted elements, displaying it correctly in your application.

const { Diagram } = ej.diagrams;

const mxGraphXMLData = `
<mxGraphModel dx="933" dy="555" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1040" pageHeight="300" math="0" shadow="0">
      <root>
        <mxCell id="0" />
        <mxCell id="1" parent="0" />
        <mxCell id="lXHuHNEkXH5VenCzG6mj-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="lXHuHNEkXH5VenCzG6mj-5" target="lXHuHNEkXH5VenCzG6mj-6" edge="1">
          <mxGeometry relative="1" as="geometry">
            <mxPoint x="170" y="160" as="targetPoint" />
          </mxGeometry>
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="lXHuHNEkXH5VenCzG6mj-6" target="lXHuHNEkXH5VenCzG6mj-7" edge="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-24" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="lXHuHNEkXH5VenCzG6mj-7" target="lXHuHNEkXH5VenCzG6mj-8" edge="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=2;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" source="lXHuHNEkXH5VenCzG6mj-8" target="lXHuHNEkXH5VenCzG6mj-9" edge="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-17" value="YES" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="lXHuHNEkXH5VenCzG6mj-16" vertex="1" connectable="0">
          <mxGeometry relative="1" as="geometry">
            <mxPoint as="offset" />
          </mxGeometry>
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fillColor=#f8cecc;strokeColor=#b85450;strokeWidth=2;" parent="1" source="lXHuHNEkXH5VenCzG6mj-8" target="lXHuHNEkXH5VenCzG6mj-11" edge="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-19" value="NO" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="lXHuHNEkXH5VenCzG6mj-18" vertex="1" connectable="0">
          <mxGeometry relative="1" as="geometry">
            <mxPoint as="offset" />
          </mxGeometry>
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="lXHuHNEkXH5VenCzG6mj-9" target="lXHuHNEkXH5VenCzG6mj-10" edge="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="lXHuHNEkXH5VenCzG6mj-11" target="lXHuHNEkXH5VenCzG6mj-12" edge="1">
          <mxGeometry relative="1" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-5" value="Order Received" style="ellipse;html=1;aspect=fixed;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;fontStyle=1;strokeWidth=2;shadow=0;" parent="1" vertex="1">
          <mxGeometry x="60" y="130" width="40" height="40" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-6" value="Retrieve Data" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontStyle=0;strokeWidth=2;" parent="1" vertex="1">
          <mxGeometry x="180" y="120" width="120" height="60" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-7" value="Approve Data" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontStyle=0;strokeWidth=2;" parent="1" vertex="1">
          <mxGeometry x="380" y="120" width="120" height="60" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-8" value="Approved?" style="rhombus;whiteSpace=wrap;html=1;fontStyle=0;labelPosition=center;verticalLabelPosition=middle;align=center;verticalAlign=middle;strokeWidth=2;" parent="1" vertex="1">
          <mxGeometry x="580" y="120" width="80" height="60" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-9" value="Order Items" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontStyle=0;strokeWidth=2;" parent="1" vertex="1">
          <mxGeometry x="740" y="60" width="120" height="60" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-10" value="Order Processed" style="ellipse;html=1;aspect=fixed;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;strokeWidth=4;fontStyle=1" parent="1" vertex="1">
          <mxGeometry x="940" y="70" width="40" height="40" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-11" value="Send Rejection Mail" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontStyle=0;strokeWidth=2;" parent="1" vertex="1">
          <mxGeometry x="740" y="180" width="120" height="60" as="geometry" />
        </mxCell>
        <mxCell id="lXHuHNEkXH5VenCzG6mj-12" value="Order Not Processed" style="ellipse;html=1;aspect=fixed;labelPosition=center;verticalLabelPosition=bottom;align=center;verticalAlign=top;strokeWidth=4;fontStyle=1" parent="1" vertex="1">
          <mxGeometry x="940" y="190" width="40" height="40" as="geometry" />
        </mxCell>
      </root>
    </mxGraphModel>
`;

// This function loads XML data, parses it into EJ2 Diagram elements, and renders the diagram.
try {
  const xmlDocument = new window.DOMParser().parseFromString(
    mxGraphXMLData,
    'application/xml'
  );
  const { nodes, connectors } = parseMxGraph(xmlDocument);

  const diagram = new Diagram({
    width: '100%',
    height: '600px',
    nodes,
    connectors,
    snapSettings: { constraints: ej.diagrams.SnapConstraints.All },
  });

  diagram.appendTo('#element');
  diagram.dataBind();
  diagram.refresh();
} catch (error) {
  console.error(error);
}

// Parses mxGraph XML to extract nodes and connectors to be converted into EJ2 Diagram format
function parseMxGraph(xmlDoc) {
  const mxCells = Array.from(xmlDoc.querySelectorAll('mxCell'));
  const nodes = [];
  let connectors = [];
  const nodeMap = {};
  const childMap = {};
  const containerNodes = [];
  const groupDefs = [];

  // Iterate over mxCells to categorize them into nodes and connectors
  mxCells.forEach((cell) => {
    const style = cell.getAttribute('style') || '';
    const styles = parseStyleString(style);
    const isVertex = cell.getAttribute('vertex') === '1';
    const isEdge = cell.getAttribute('edge') === '1';
    let isLabel = cell.getAttribute('connectable') === '0';
    const isGroup = style.includes('group');
    const id = cell.getAttribute('id');
    const parent = cell.getAttribute('parent');

    // Group shapes are treated as vertices, not labels
    if (isGroup) isLabel = false;

    // Convert mxGraph vertex cells (excluding labels) to EJ2 nodes
    if (isVertex && !isLabel) {
      const isContainer =
        'containerType' in styles || style.includes('swimlane');
      const node = createNode(cell, styles);
      nodeMap[id] = node;

      if (parent) {
        childMap[parent] = childMap[parent] || [];
        childMap[parent].push(id);
      }

      if (isContainer) {
        containerNodes.push({ id, node });
      } else if (isGroup) {
        groupDefs.push({ id, node });
      } else {
        nodes.push(node);
      }
    } else if (isEdge) {
      // Convert mxGraph edge cells to EJ2 connectors
      connectors.push(createConnector(cell, styles, mxCells));
    }
  });

  // Associate children nodes with their parent containers or groups
  attachChildrenToParents(childMap, nodeMap);

  // Finalize the list of nodes and connectors for the EJ2 diagram
  finalizeNodesAndConnectors(
    nodes,
    connectors,
    containerNodes,
    groupDefs,
    childMap
  );

  return { nodes, connectors };
}

// Parses a style string into an object for easier manipulation while creating EJ2 elements
function parseStyleString(str) {
  const out = {};
  str.split(';').forEach((token) => {
    if (!token) return;
    const [k, v] = token.split('=');
    if (v === undefined) out.shape = k;
    else out[k] = v;
  });
  return out;
}

// Creates EJ2 diagram nodes from mxGraph cells based on style and geometric data
function createNode(cell, s) {
  const geo = cell.querySelector('mxGeometry');
  const x = +geo.getAttribute('x') || 0;
  const y = +geo.getAttribute('y') || 0;
  const w = +geo.getAttribute('width') || 100;
  const h = +geo.getAttribute('height') || 100;

  const shape = resolveShape(s);
  const node = configureNodeBase(cell, w, h, x, y, shape, s);

  const annotationStyle = parseFontStyle(s.fontStyle);
  const rawContent = decodeAnnotationContent(cell, s);
  const annotation = configureAnnotation(s, shape, rawContent, annotationStyle);
  node.annotations.push(annotation);

  if (shape.shape === 'Decision') {
    addDecisionPorts(node);
  }

  return node;
}

// Configures node base properties from geometric and style data
function configureNodeBase(cell, width, height, offsetX, offsetY, shape, s) {
  return {
    id: cell.getAttribute('id'),
    width,
    height,
    offsetX: offsetX + width / 2,
    offsetY: offsetY + height / 2,
    shape,
    style: {
      fill: s.fillColor || '#ffffff',
      strokeColor: s.strokeColor || '#000000',
      strokeWidth: +(s.strokeWidth || 1),
    },
    annotations: [],
  };
}

// Decodes HTML entity encoded content from mxGraph XML
function decodeAnnotationContent(cell, s) {
  const isHtml = s.html === '1';
  const rawContent = isHtml
    ? decodeHtmlEntities(cell.getAttribute('value') || '')
    : cell.getAttribute('value') || '';
  return rawContent;
}

// Resolves the appropriate shape for EJ2 from mxGraph styling
function resolveShape(s) {
  const shapeLookup = {
    ellipse: { type: 'Basic', shape: 'Ellipse' },
    rhombus: { type: 'Flow', shape: 'Decision' },
    rectangle: { type: 'Basic', shape: 'Rectangle' },
  };
  const shape = shapeLookup[s.shape] || shapeLookup.rectangle;

  if (shape.shape === 'Rectangle' && s.rounded === '1') {
    shape.cornerRadius = 8;
  }

  return shape;
}

// Configures annotation based on position and style information from mxGraph
function configureAnnotation(s, shape, rawContent, annotationStyle) {
  const annotation = {
    content: rawContent,
    style: annotationStyle,
  };

  if (shape.shape === 'Ellipse') {
    annotation.offset = { x: 0.5, y: 1 };
    annotation.margin = { top: 5 };
    annotation.verticalAlignment = 'Top';
    annotation.fontWeight = '500';
    annotation.width = 'auto';
  } else {
    annotation.verticalAlignment =
      s.verticalLabelPosition === 'bottom' ? 'Bottom' : 'Center';
  }

  return annotation;
}

// Adding ports to decision nodes to support connector bindings
function addDecisionPorts(node) {
  node.ports = [
    { id: 'portTop', offset: { x: 0.5, y: 0 } },
    { id: 'portBottom', offset: { x: 0.5, y: 1 } },
  ];
}

// Creates EJ2 diagram connectors from mxGraph edges
function createConnector(cell, s, mxCells) {
  const connector = {
    id: cell.getAttribute('id'),
    type: 'Orthogonal',
    sourceID: cell.getAttribute('source'),
    targetID: cell.getAttribute('target'),
    style: {
      strokeColor: s.strokeColor || '#000000',
      strokeWidth: +(s.strokeWidth || 1),
    },
  };

  // Attach connector labels for YES/NO conditions
  attachLabelToConnector(connector, cell, mxCells);

  return connector;
}

// Associates labels from edge children to their connectors
function attachLabelToConnector(connector, cell, mxCells) {
  const labelCell = mxCells.find(
    (c) =>
      c.getAttribute('parent') === connector.id &&
      c.getAttribute('connectable') === '0' &&
      c.hasAttribute('value')
  );

  if (labelCell) {
    const labelStyle = parseFontStyle(
      parseStyleString(labelCell.getAttribute('style')).fontStyle
    );
    const label = labelCell.getAttribute('value').toLowerCase();

    connector.annotations = [
      {
        content: labelCell.getAttribute('value'),
        style: labelStyle,
      },
    ];

    if (label === 'yes') {
      connector.sourcePortID = 'portTop';
    } else if (label === 'no') {
      connector.sourcePortID = 'portBottom';
    }
  }
}

// Map children to parents within the diagram, aiding in proper hierarchy structuring
function attachChildrenToParents(childMap, nodeMap) {
  Object.entries(childMap).forEach(([parentId, childIds]) => {
    if (nodeMap[parentId]) {
      nodeMap[parentId].children = childIds;
    }
  });
}

// Finalize and filter out containers from regular nodes and connectors for accurate rendering
function finalizeNodesAndConnectors(
  nodes,
  connectors,
  containerNodes,
  groupDefs,
  childMap
) {
  containerNodes.forEach(({ node }) => {
    nodes.push(node);
  });

  groupDefs.forEach(({ id }) => {
    const children = childMap[id] || [];
    if (children.length) {
      nodes.push({ id, children });
    }
  });

  const containerIds = new Set(containerNodes.map((c) => c.id));
  connectors = connectors.filter(
    (c) => !containerIds.has(c.sourceID) && !containerIds.has(c.targetID)
  );
}

// Interprets font style from mxGraph format for EJ2 rendering
function parseFontStyle(fontStyleValue) {
  const style = {};
  const styleCode = parseInt(fontStyleValue || '0', 10);

  if ((styleCode & 1) === 1) style.bold = true;
  if ((styleCode & 2) === 2) style.italic = true;
  if ((styleCode & 4) === 4) style.textDecoration = 'Underline';

  style.textAlign = 'Left';
  return style;
}

// Decodes HTML entities in JavaScript
function decodeHtmlEntities(text) {
  const div = document.createElement('div');
  div.innerHTML = text;
  return div.innerText || div.textContent;
}
<!DOCTYPE html><html lang="en"><head>
    <title>EJ2 Diagram</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Typescript UI Controls">
    <meta name="author" content="Syncfusion">
    <link href="index.css" rel="stylesheet">
    
    <link href="https://cdn.syncfusion.com/ej2/30.1.37/ej2-base/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/30.1.37/ej2-buttons/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/30.1.37/ej2-popups/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/30.1.37/ej2-splitbuttons/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/30.1.37/ej2-diagrams/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/30.1.37/ej2-navigations/styles/fabric.css" rel="stylesheet">
    
<script src="https://cdn.syncfusion.com/ej2/30.1.37/dist/ej2.min.js" type="text/javascript"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

<body>
    
    <div id="container">
        <div id="element"></div>
    </div>


<script>
var ele = document.getElementById('container');
if(ele) {
  ele.style.visibility = "visible";
}   
      </script>
<script src="index.js" type="text/javascript"></script>
</body></html>

Next, let’s closely examine how the mxGraph diagram elements are rendered and how you can achieve the same output using Syncfusion® JavaScript Diagram.

Migrating from mxGraph to Syncfusion® JavaScript Diagram control

The following examples demonstrating how to redefine common diagramming elements from mxGraph using Syncfusion® JavaScript Diagram.

Creating Basic Nodes

In mxGraph, basic nodes are created using vertices.

const graph = new mxGraph(container);
const parent = graph.getDefaultParent();

graph.getModel().beginUpdate();
try {
const v1 = graph.insertVertex(parent, null, 'Hello', 20, 20, 80, 30);
} finally {
graph.getModel().endUpdate();
}

In Syncfusion® JavaScript Diagram, you create nodes by specifying their properties such as position and size.

const diagram = new ej.diagrams.Diagram({
  width: '100%',
  height: '600px',
  nodes: [{
    id: 'node1',
    offsetX: 60,
    offsetY: 35,
    width: 80,
    height: 30,
    annotations: [{ content: 'Hello' }]
  }]
});
diagram.appendTo('#diagram');

Connecting Diagram Elements

In mxGraph, connections between elements are formed by inserting edges between vertices.

const v1 = graph.insertVertex(parent, null, 'Node1', 20, 20, 80, 30);
const v2 = graph.insertVertex(parent, null, 'Node2', 200, 150, 80, 30);
const e1 = graph.insertEdge(parent, null, '', v1, v2);

In Syncfusion® JavaScript Diagram, you create connectors by specifying the source and target node IDs.

const nodes = [
  { id: 'node1', offsetX: 60, offsetY: 35, width: 80, height: 30, annotations: [{ content: 'Node1' }] },
  { id: 'node2', offsetX: 200, offsetY: 150, width: 80, height: 30, annotations: [{ content: 'Node2' }] }
];
const connectors = [
  { id: 'connector1', sourceID: 'node1', targetID: 'node2' }
];

Annotation Creation and Formatting

In mxGraph, styling labels requires editing a style object and applying it to the node. Custom label positioning needs extra configuration and is less straightforward.

const vertex = graph.insertVertex(parent, null, 'Decision', 100, 100, 80, 40);
const style = graph.getStylesheet().getDefaultVertexStyle();
style[mxConstants.STYLE_FONTCOLOR] = '#FF0000';
style[mxConstants.STYLE_FONTSTYLE] = mxConstants.FONT_BOLD;
style[mxConstants.STYLE_FONTSIZE] = 14;
style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE;
graph.getModel().setStyle(vertex, mxStyleRegistry.getName(style));

In Syncfusion® JavaScript Diagram, annotations are defined within the annotations array of a node, allowing for straightforward customization:

const node = {
  id: 'decisionNode',
  offsetX: 300,
  offsetY: 200,
  width: 100,
  height: 60,
  shape: { type: 'Flow', shape: 'Decision' },
  annotations: [{
    content: 'Decision',
    style: {
      color: '#FF0000',
      bold: true,
      fontSize: 14
    },
    offset: { x: 0.5, y: 1.2 },
    verticalAlignment: 'Top',
    horizontalAlignment: 'Center',
    margin: { top: 10 }
  }]
};

Using Ports

In mxGraph, ports are additional vertices within a parent vertex.

const vertex = graph.insertVertex(parent, null, 'Node 1', 100, 100, 80, 30);
const port = graph.insertVertex(vertex, null, '', 1, 0.5, 10, 10,
  'shape=ellipse;perimter=ellipsePerimeter;portConstraint=east;');
port.geometry.offset = new mxPoint(-5, -5);
port.geometry.relative = true;

In Syncfusion® JavaScript Diagram, ports are similarly enabled by defining them within a node, specifying offsets and shapes.

var diagram = new ej.diagrams.Diagram({
  width: '100%',
  height: '600px',
  nodes: [{
    id: 'node1',
    offsetX: 90,
    offsetY: 60,
    width: 100,
    height: 40,
    annotations: [{ content: 'Node 1' }],
    ports: [{
      id: 'port1',
      offset: { x: 1, y: 0.5 },
      visibility: ej.diagrams.PortVisibility.Visible,
      shape: 'Circle',
      width: 10,
      height: 10
    }]
  }]
});
diagram.appendTo('#diagram');

Applying Layouts

In mxGraph, layouts like hierarchical are applied by using specific layout classes.

var v1 = graph.insertVertex(parent, null, 'Start', 0, 0, 100, 40);
var v2 = graph.insertVertex(parent, null, 'Middle', 0, 0, 100, 40);
var v3 = graph.insertVertex(parent, null, 'End', 0, 0, 100, 40);
graph.insertEdge(parent, null, '', v1, v2);
graph.insertEdge(parent, null, '', v2, v3);
var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_NORTH);
layout.execute(parent);

In Syncfusion® JavaScript Diagram, layouts are defined in the diagram using layout property.

ej.diagrams.Diagram.Inject(ej.diagrams.HierarchicalTree);
var diagram = new ej.diagrams.Diagram({
  width: '100%',
  height: '600px',
  layout: {
    type: 'HierarchicalTree',
    horizontalSpacing: 25,
    verticalSpacing: 35
  },
  nodes: [
    {
      id: 'node1',
      annotations: [{ content: 'Parent' }]
    },
    {
      id: 'node2',
      annotations: [{ content: 'Child 1' }]
    },
    {
      id: 'node3',
      annotations: [{ content: 'Child 2' }]
    }
  ],
  connectors: [
    { id: 'connector1', sourceID: 'node1', targetID: 'node2' },
    { id: 'connector2', sourceID: 'node1', targetID: 'node3' }
  ]
});
diagram.appendTo('#diagram');

Saving and Loading Diagrams

In mxGraph, the graph model is serialized and deserialized using XML.

// Save the graph model to XML string
function saveGraph(graph) {
  const encoder = new mxCodec();
  const node = encoder.encode(graph.getModel());
  const xml = mxUtils.getXml(node);
  return xml;
}

// Load the graph model from XML string
function loadGraph(graph, xml) {
  const doc = mxUtils.parseXml(xml);
  const codec = new mxCodec(doc);
  codec.decode(doc.documentElement, graph.getModel());
  graph.refresh();
}

In Syncfusion® JavaScript Diagram, diagram states are saved and loaded using JSON strings for persistence.

const savedDiagram = diagram.saveDiagram(); // returns JSON string
diagram.loadDiagram(savedDiagram); // restores diagram

Event Handling

In mxGraph, you handle events by adding listeners to the graph.

graph.addListener(mxEvent.CLICK, function(sender, evt) {
  const cell = evt.getProperty('cell');
  if (cell != null) {
    console.log('Cell clicked:', cell.value);
  }
});

In Syncfusion® JavaScript Diagram, event handling is configured with function callbacks in the diagram.

var diagram = new ej.diagrams.Diagram({
  width: '100%',
  height: '600px',
  click: function(args) {
    if (args.element && args.element.annotations) {
      console.log('Node clicked:', args.element.annotations[0].content);
    }
  }
});
diagram.appendTo('#diagram');

Diagram Canvas Configuration and Customization

Configuring the mxGraph diagram canvas often involves manual calculations and numerous API calls. In comparison, Syncfusion® JavaScript Diagram provides a more straightforward approach with built-in properties and methods.

Grid Lines

In mxGraph, enabling and customizing grid lines involves setting various properties.

graph.setGridEnabled(true);
graph.setGridSize(10);
graph.view.gridColor = '#e0e0e0';

In Syncfusion® JavaScript Diagram, grid lines are configured using the snapSettings property, allowing for a straightforward setup while also supporting additional customization options that are self-explanatory:

var diagram = new ej.diagrams.Diagram({
    width: '100%', height: '600px',
    snapSettings: {
        constraints: ej.diagrams.SnapConstraints.ShowLines,
        horizontalGridlines: { lineColor: '#e0e0e0', lineDashArray: '2,2' },
        verticalGridlines: { lineColor: '#e0e0e0', lineDashArray: '2,2' }
    }
});
diagram.appendTo('#element');

Ruler

To use rulers in mxGraph, you’ll need to undertake custom development since it lacks native support for this feature. In contrast, Syncfusion® Diagram provides built-in ruler support that can be easily activated with a straightforward property setting:

var diagram = new ej.diagrams.Diagram({
    width: '100%', height: '600px',
    rulerSettings: { showRulers: true }
});
diagram.appendTo('#element');

Page Settings

In mxGraph, setting up page dimensions and appearance involves setting properties on the graph object like pageVisible, pageBreaksVisible, and setPageSize.

graph.pageVisible = true;
graph.pageBreaksVisible = true;
graph.setPageSize(new mxRectangle(0, 0, 800, 600));
graph.container.style.backgroundColor = '#f5f5f5';

In Syncfusion® JavaScript Diagram (ej2), the pageSettings property provides a straightforward way to configure page settings, including support for multiple pages and showing page breaks.

var diagram = new ej.diagrams.Diagram({
    width: '100%', height: '600px',
    pageSettings: {
        width: 800, height: 600,
        background: { color: '#f5f5f5' },
        showPageBreaks: true,
        multiplePage: true // Enables multiple page feature
    }
});
diagram.appendTo('#element');

Scroll Settings

In mxGraph, scroll behavior is primarily handled through the container’s CSS and enabling panning. There are no built-in APIs for configuring advanced scroll behavior like auto-scroll or scroll boundaries.

// Basic scroll setup via container styling and panning
graph.setPanning(true);
graph.panningHandler.useLeftButtonForPanning = true;
graph.container.style.overflow = 'auto';

In Syncfusion® JavaScript Diagram, the scrollSettings property provides detailed control over scroll behavior, including auto-scroll, scroll limits, and scroll frequency.

var diagram = new ej.diagrams.Diagram({
width: '100%', height: '600px',
scrollSettings: {
scrollLimit: 'Infinity',
canAutoScroll: true,
autoScrollBorder: { left: 20, top: 20, right: 20, bottom: 20 },
autoScrollFrequency: 100
}
});
diagram.appendTo('#element');

Symbol Palette

Unlike Syncfusion® JavaScript Diagram, mxGraph does not offer a built-in SymbolPalette UI component. You must manually build a palette using HTML elements, style them, and use mxUtils.makeDraggable() for drag-and-drop integration. Below is an example of easily creating a palette with Syncfusion® JavaScript Diagram.

const palette = new ej.diagrams.SymbolPalette({
  palettes: [{
    id: 'flowShapes',
    expanded: true,
    symbols: [
      { id: 'Terminator', shape: { type: 'Flow', shape: 'Terminator' } },
      { id: 'Process', shape: { type: 'Flow', shape: 'Process' } }
    ],
    title: 'Flow Shapes'
  }]
});
palette.appendTo('#palette');

Diagram Interactions

Syncfusion® JavaScript Diagram provides easy support for key interactions like zooming, panning, selection, dragging, resizing, rotating, and keyboard navigation with little setup. Its user-friendly API and clear properties simplify implementation. In contrast, mxGraph usually needs more manual setup and extra coding to achieve similar outcomes.

Zooming

In mxGraph, implementing zoom functionality involves invoking specific methods and managing the zoom scale manually.

// Zoom in
graph.zoomIn();

// Zoom out
graph.zoomOut();

// Zoom to specific scale
graph.zoomTo(1.5); // 150% zoom

With Syncfusion® JavaScript Diagram, you can easily control zooming using built-in methods or through the user interface.

// Zoom in
diagram.zoomTo({ type: 'ZoomIn', zoomFactor: 0.2 });

// Zoom out
diagram.zoomTo({ type: 'ZoomOut', zoomFactor: 0.2 });

// Zoom to specific scale
diagram.zoomTo({ type: 'Zoom', zoomFactor: 1.5 }); // 150% zoom

Panning

In mxGraph, panning is enabled by setting the panning handler and configuring the mouse button for panning:

// Enable panning
graph.setPanning(true);
graph.panningHandler.useLeftButtonForPanning = true;

In Syncfusion® JavaScript Diagram, to enable panning, you need to set the tool property to include ZoomPan:

// Enable panning
diagram.tool = ej.diagrams.DiagramTools.ZoomPan;

Undo / Redo

In mxGraph, enabling undo and redo requires setting up an mxUndoManager and wiring it to the model events.

const undoManager = new mxUndoManager();
graph.getModel().addListener(mxEvent.UNDO, undoManager);
undoManager.undo();
undoManager.redo();

Syncfusion® JavaScript Diagram provides built-in undo and redo commands, requiring no additional setup:

diagram.undo();
diagram.redo();

In addition to the interactions showcased above, Syncfusion® JavaScript Diagram supports many more intuitive and powerful interactions—such as snapping, constraints, drag-and-drop from palettes, tooltips, and context menus. All of these can be enabled or customized with minimal configuration, making it easy to deliver a seamless and interactive diagramming experience.

NOTE

To know more about the Interactions in Syncfusion® JavaScript Diagram, refer to the documentation.

Keyboard Interactions

Keyboard shortcuts enhance user productivity by allowing quick access to common actions. While both mxGraph and Syncfusion® JavaScript Diagram support keyboard interactions, Syncfusion® offers a more extensive set of built-in commands and easier customization.

The following table compares common keyboard shortcuts and their implementations in mxGraph and Syncfusion® JavaScript Diagram:

Action mxGraph Implementation Syncfusion® JavaScript Diagram Implementation
Select All Requires custom key handler setup using mxKeyHandler or mxDefaultKeyHandler Built-in support: Ctrl + A selects all nodes/connectors
Copy Custom implementation needed using mxClipboard.copy and key handler Built-in support: Ctrl + C copies selected elements
Paste Custom implementation needed using mxClipboard.paste and key handler Built-in support: Ctrl + V pastes copied elements
Cut Custom implementation needed using mxClipboard.cut and key handler Built-in support: Ctrl + X cuts selected elements
Undo Requires setting up mxUndoManager and binding Ctrl + Z manually Built-in support: Ctrl + Z undoes the last action
Redo Requires setting up mxUndoManager and binding Ctrl + Y manually Built-in support: Ctrl + Y redoes the last undone action
Delete Custom implementation needed to bind Delete key to remove selected elements Built-in support: Delete key removes selected elements
Label Editing Requires custom event handling for label editing initiation and termination Built-in support: F2 to start editing, Esc to cancel
Zoom In/Out Custom implementation needed to bind Ctrl + MouseWheel for zooming Built-in support: Ctrl + MouseWheel zooms in/out
Nudge Elements Custom implementation needed to move elements with arrow keys Built-in support: Arrow keys move elements by 1px; Shift + Arrow moves by 5px
Group/Ungroup Custom implementation needed to group/ungroup elements with key bindings Built-in support: Ctrl + G to group, Ctrl + Shift + U to ungroup
Rotate Elements Custom implementation needed to rotate elements with key bindings Built-in support: Ctrl + R rotates clockwise, Ctrl + L rotates counter-clockwise
Flip Elements Custom implementation needed to flip elements with key bindings Built-in support: Ctrl + H flips horizontally, Ctrl + J flips vertically
Align Text Custom implementation needed to align text with key bindings Built-in support: Use Ctrl + Shift + L/C/R/J for horizontal and Ctrl + Shift + E/M/V for vertical text alignment.

Enhanced Interactions in Syncfusion JavaScript Diagram over mxGraph

Syncfusion® JavaScript Diagram offers a comprehensive set of built-in commands for common diagramming tasks. Unlike mxGraph, which often needs lengthy manual code, these commands can be used with minimal coding, enhancing productivity and maintainability.

Fit to Page

In mxGraph, fitting a diagram to the page involves calculating bounds, determining scale, and manually adjusting the viewport.

const bounds = graph.getGraphBounds();
const scale = Math.min(container.clientWidth / bounds.width, container.clientHeight / bounds.height);
graph.view.setScale(scale);
graph.view.setTranslate(-bounds.x, -bounds.y);

In Syncfusion® JavaScript Diagram, this can be achieved with a single method call:

diagram.fitToPage();

Bring to Center

Centering an element in mxGraph involves manual calculations based on the element’s bounds and viewport dimensions.

const bounds = graph.getCellBounds(cell);
const dx = container.clientWidth / 2 - (bounds.x + bounds.width / 2);
const dy = container.clientHeight / 2 - (bounds.y + bounds.height / 2);
graph.view.setTranslate(dx, dy);

In Syncfusion® JavaScript Diagram, this is simplified with a direct API method:

diagram.bringToCenter('node1');

Bring into View

In mxGraph, bringing an element into view can be done using the scrollCellToVisible method, which scrolls the diagram to reveal the specified cell.

graph.scrollCellToVisible(cell);

In Syncfusion® JavaScript Diagram, bringing a node into view is very similar to doing so in mxGraph.

diagram.bringIntoView('node1');

In addition to the commands shown above, Syncfusion® JavaScript Diagram provides a full set of built-in commands for easy diagram editing and interaction. These commands cover common tasks for arranging, transforming, navigating, and managing diagram elements, accessible via straightforward API calls or user-friendly keyboard shortcuts.

NOTE

To learn more about the built-in commands in Syncfusion® JavaScript Diagram, refer to our documentation.