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
- pnpm
- yarn
npm install @joint/layout-msagl
pnpm add @joint/layout-msagl
yarn add @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
-
Install the MSAGL layout open-source package (
@joint/layout-msagl) using NPM:npm add @joint/layout-msagl -
Navigate to the newly created
node_modulesfolder. -
Copy the
@joint/layout-msagl/dist/umd/index.jsfile 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)
- Navigate to the
node_modulesfolder. - Copy the
@msagl/core/dist.min.jsfile and paste it into the root of your package.
Include index.js plus its dependency dist.min.js in your HTML:
- JointJS
- JointJS+
<script src="joint.js"></script>
<script src="dist.min.js"></script>
<script src="index.js"></script>
Access MSAGL through the joint.layout namespace:
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/layerSeparationfor density.
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
edgeRoutingModeis set toRectilinear, self‑edges use a configurable vertical offset controlled byrectilinearSelfEdgeOffset(default10). This is a stop‑gap until upstreammsagljsprovides 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.Paddingoption 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: 1andid: '1'are considered the same.