Skip to main content
Version: 4.2

MSAGL layout

The MSAGL layout package wraps the Microsoft Automatic Graph Layout (MSAGL) Sugiyama engine so you can arrange JointJS graphs with hierarchical routing, bundled splines, and nested subgraphs. It ships as @joint/layout-msagl and works with JointJS 4.0 and newer.

The package exports a single function, layout(), plus enums for routing direction and edge routing modes. This page focuses on installation, everyday usage, and how to make the most of the available options.

Installation​

Install the package from a registry and add it alongside @joint/core in your bundle:

npm install @joint/layout-msagl

Once the dependency is in place, import what you need:

import { layout, EdgeRoutingMode, LayerDirectionEnum } from '@joint/layout-msagl';

The following example illustrates the layout() function. The first parameter can be a dia.Graph or an array of dia.Cell that we want to lay out. The second parameter is an object that contains various options for configuring the layout:

import { layout, EdgeRoutingMode, LayerDirectionEnum } from '@joint/layout-msagl';

// ...

const { bbox } = layout(graph, {
layerDirection: LayerDirectionEnum.TB,
layerSeparation: 60,
nodeSeparation: 30,
edgeRoutingMode: EdgeRoutingMode.Rectilinear,
x: 20,
y: 20,
clusterPadding: { left: 20, right: 20, top: 20, bottom: 20 }
});

console.log('x:', bbox.x, 'y:', bbox.y);
console.log('width:', bbox.width, 'height:', bbox.height);

x and y shift the top-left origin of the layout, while clusterPadding reserves space inside any nested subgraphs. Pass either a number for uniform padding or a dia.Sides object when you need per-side values.

There is also a UMD version available

Place the UMD distribution of the plugin into the root of your package.

One way to do that is via NPM
  1. Install the MSAGL layout open-source package (@joint/layout-msagl) using NPM:

    npm add @joint/layout-msagl
  2. Navigate to the newly created node_modules folder.

  3. Copy the @joint/layout-msagl/dist/umd/index.js file and paste it into the root of your package.

Place the source code of the required dependency - @msagl/core/dist.min.js - into the root of your package.

One way to do that is via NPM (continuing from above)
  1. Navigate to the node_modules folder.
  2. Copy the @msagl/core/dist.min.js file and paste it into the root of your package.

Include index.js plus its dependency dist.min.js in your HTML:

index.html
<script src="joint.js"></script>
<script src="dist.min.js"></script>
<script src="index.js"></script>

Access MSAGL through the joint.layout namespace:

index.js
const { bbox } = joint.layout.MSAGL.layout(graph, {
edgeRoutingMode: joint.layout.MSAGL.EdgeRoutingMode.Rectilinear
});
console.log('x:', bbox.x, 'y:', bbox.y);
console.log('width:', bbox.width, 'height:', bbox.height);

Routing modes​

Use the following sandbox to experiment with MSAGL routing on a simple adjacency list. Edit the JSON to add or remove nodes and switch between rectilinear and spline-bundled edges with the dropdown. The layout recalculates each time you click Layout or change the routing mode.

Measuring labels​

The MSAGL layout engine needs to know how much space to reserve for labels. By default it reads the labelSize property from elements and links:

link.set('labelSize', { width: 80, height: 20 });
parent.set('labelSize', { width: 120, height: 24 });

If you do not provide a size, getLabelSize returns undefined and the label is ignored by the layout. You can supply your own sizing logic for dynamic content:

layout(graph, {
getLabelSize: (cell) => {
const text = cell.attr('label/text') || cell.label(0)?.attrs?.text?.text || '';
return text ? { width: text.length * 8, height: 22 } : undefined;
}
});

This example demonstrates how MSAGL handles label measurement in layouts. The interactive checkbox allows you to toggle between two modes:

  • With label measurement (checkbox checked): Labels are measured and their dimensions are considered during layout, ensuring proper spacing and preventing overlaps
  • Without label measurement (checkbox unchecked): Labels are ignored during layout calculations, resulting in a more compact but potentially overlapping arrangement - in case of subgraphs (parents) the headings are not measured and no space is reserved for them.

Toggle the checkbox to see the immediate visual difference in how the layout algorithm accounts for label space.

Node Padding​

In msagljs the padding option which controls the distance between edges and obstacles (nodes) is currently not functioning, so edges can bend too close to node, resulting in a less readable layout. The demo below toggles a pragmatic workaround:

  • Inflate nodes in getSize() so edges route a bit farther away.
  • Shift those nodes back by half of the inflation in setPosition() to keep visuals centered.
  • Use the paper’s boundary connection point so links remain correctly attached after the shift.
  • This affects edge‑to‑node clearance, not overall spacing; adjust nodeSeparation/layerSeparation for density.
warning

This workaround is only effective for non-cluster nodes since cluster nodes are resized directly by MSAGL - applying the offset to parents would misalign the cluster.

Caveats​

  • Rectilinear self‑loops – When edgeRoutingMode is set to Rectilinear, self‑edges use a configurable vertical offset controlled by rectilinearSelfEdgeOffset (default 10). This is a stop‑gap until upstream msagljs provides native rectilinear self‑loop geometry.
  • Subgraph resizing – Parent elements that embed other elements are resized by the layout to tightly pack all their children.
  • Subgraph layout direction - Layout inside subgraphs is always set to TB (Top-to-Bottom) direction, as other directions can cause layout issues.
  • Link labels in subgraphs – Link labels within subgraphs may be positioned incorrectly, despite the layout correctly reserving space for them.
  • Obstacle padding (first‑bend distance) – MSAGL exposes an edgeRoutingSettings.Padding option intended to keep edges a minimum distance away from obstacles (nodes) and thus control where the first bend occurs. In msagljs this setting is currently not working - refer to the Node Padding section for a workaround.
  • ID handling - MSAGL expects IDs to be string values so id: 1 and id: '1' are considered the same.

Stay in the know

Be where thousands of diagramming enthusiasts meet

Star us on GitHub