fCoSE Layout for Cytoscape.js
cytoscape-fcose is a high-performance layout extension for Cytoscape.js, implementing the fCoSE (fast Compound Spring Embedder) algorithm. Developed by the i-Vis Lab at Bilkent University, it is significantly faster than its predecessor, CoSE, offering up to 2x speed improvement while maintaining similar aesthetic quality for graph visualizations. The current stable version is 2.2.0, with releases occurring a few times a year, indicating active maintenance. A key differentiator is its robust support for compound graphs and an extensive set of user-defined constraints including fixed node positions, alignment (vertical/horizontal), and relative placement, which can also be applied incrementally. It achieves this by combining spectral layout techniques with force-directed methods, making it suitable for complex network visualizations where both speed and structural clarity are paramount.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'layout')
cause The `fcose` layout extension has not been properly registered with your Cytoscape.js instance before attempting to use it.fixEnsure you call `cytoscape.use(fcose);` after importing the `cytoscape-fcose` module and before initializing any Cytoscape.js instances or calling `cy.layout({ name: 'fcose' })`. -
Error: The layout 'fcose' is not registered.
cause The `fcose` layout extension module was imported but not correctly registered with Cytoscape.js, or the registration happened after an attempt to use the layout.fixVerify that `import fcose from 'cytoscape-fcose';` and `cytoscape.use(fcose);` are executed at an appropriate place in your application setup, typically at the entry point or before any graph initialization. -
Error: `alignmentConstraint` must be given in most compact form. Example: `['n1', 'n2', 'n3']` instead of `['n1', 'n2'], ['n1', 'n3']`.
cause Your `alignmentConstraint` configuration contains redundant or non-compact array definitions for nodes that should be aligned.fixRefactor your `alignmentConstraint` to group all nodes that should be aligned in a single direction (vertical or horizontal) into one sub-array. For example, change `vertical: [['n1', 'n2'], ['n1', 'n3']]` to `vertical: [['n1', 'n2', 'n3']]`.
Warnings
- breaking Version 2.0.0 introduced significant new features including user-specified constraint support (fixed node, alignment, relative placement) and per-element values for options like `nodeRepulsion`, `edgeElasticity`, and `idealEdgeLength`. While existing configurations might still work, the structure of layout options for constraints is new and may require updating if attempting to use these advanced features.
- gotcha When defining `alignmentConstraint`, arrays for vertical or horizontal alignment must be provided in the 'most compact form'. For instance, instead of `[['n1', 'n2'], ['n1', 'n3']]` for vertical alignment, use `[['n1', 'n2', 'n3']]`.
- gotcha The `cytoscape-fcose` extension has a peer dependency on `cytoscape` version `^3.2.0`. Using an incompatible major version of Cytoscape.js (e.g., Cytoscape.js v2.x or v4.x) may lead to runtime errors or unexpected layout behavior.
- deprecated The `numeric.js` dependency was removed in v1.2.2, contributing to performance improvements. While not a direct breaking change for most users, it signifies internal refactoring.
Install
-
npm install cytoscape-fcose -
yarn add cytoscape-fcose -
pnpm add cytoscape-fcose
Imports
- fcose
import { fcose } from 'cytoscape-fcose';import fcose from 'cytoscape-fcose'; // then register with cytoscape cy.use(fcose);
- fcose (CommonJS)
const { fcose } = require('cytoscape-fcose');const fcose = require('cytoscape-fcose'); // then register with cytoscape cy.use(fcose); - LayoutOptions type (TypeScript)
import cytoscape, { LayoutOptions } from 'cytoscape'; // then use: const options: LayoutOptions = { name: 'fcose', ... };
Quickstart
import cytoscape from 'cytoscape';
import fcose from 'cytoscape-fcose';
cytoscape.use(fcose);
const cy = cytoscape({
container: document.getElementById('cy'),
elements: [
{ data: { id: 'a', parent: 'c' } },
{ data: { id: 'b', parent: 'c' } },
{ data: { id: 'c' } },
{ data: { id: 'd' } },
{ data: { id: 'e' } },
{ data: { id: 'ab', source: 'a', target: 'b' } },
{ data: { id: 'ad', source: 'a', target: 'd' } },
{ data: { id: 'de', source: 'd', target: 'e' } }
],
style: [
{ selector: 'node', style: { 'background-color': '#666', label: 'data(id)' } },
{ selector: '$node > node', style: { 'background-color': '#ccc', 'padding': '10px' } },
{ selector: 'edge', style: { 'width': 3, 'line-color': '#ccc', 'target-arrow-color': '#ccc', 'target-arrow-shape': 'triangle', 'curve-style': 'bezier' } }
]
});
const layoutOptions = {
name: 'fcose',
animate: true,
animationDuration: 500,
// Constraints example: fix node 'd' at a position
fixedNodeConstraint: [{
nodeId: 'd',
position: { x: 300, y: 100 }
}],
// Alignment constraint example: align 'a' and 'b' vertically
alignmentConstraint: {
vertical: [['a', 'b']],
horizontal: []
},
nodeRepulsion: 4500,
idealEdgeLength: 50,
edgeElasticity: 0.45,
nestingFactor: 0.1,
numIter: 2500,
initialEnergyOnIncremental: 0.3 // For incremental layout
};
cy.layout(layoutOptions).run();