Skip to content

Styling Nodes & Edges

Nodus separates what an element is (your data) from how it looks (its visual attributes). This guide is a recipe book for styling: data-driven rules, reusable classes, the full vocabulary of node and edge visuals, and styling for hover and selection states.

For the model behind it, see Styling. For the full method list, see API: Styles.

Three ways to set a look

  1. Per element — set attributes directly: node.setAttributes({ color: 'red' }).
  2. Rulesnodus.styles.addNodeRule(...) applies attributes across many elements, and can compute values from each element.
  3. Classesnodus.styles.createClass(...) defines a named look you toggle with element.addClass(name).

Rules and classes are the scalable options; reach for them whenever a style should depend on data or apply to a group.

Style rules

addNodeRule and addEdgeRule register attributes applied to elements. Values can be static, or functions of the element — which is how you drive styling from your data.

// Static rule: every node gets a base look.
nodus.styles.addNodeRule({
color: '#cbd5e1',
radius: 10,
});
// Function-valued rule: colour by team, label from data.
nodus.styles.addNodeRule({
color: (node) => node.getData('team') === 'core' ? '#4f46e5' : '#94a3b8',
text: {
content: (node) => node.getData('name'),
position: 'bottom',
},
});
// Edge rule: width and style driven by the relationship.
nodus.styles.addEdgeRule({
width: (edge) => edge.getData('kind') === 'reports-to' ? 3 : 1,
shape: { head: 'arrow' },
});

Classes

A class is a named bundle of attributes you switch on and off. Create it once, then add or remove it on elements or whole lists.

const highlight = nodus.styles.createClass({
name: 'highlight',
nodeAttributes: {
color: '#f59e0b',
halo: { color: '#fde68a', width: 8 },
},
});
// Toggle on a single element...
nodus.getNode('ada').addClass('highlight');
// ...or broadcast across a filtered list.
nodus.getNodes().filter((n) => n.getData('team') === 'core').addClass('highlight');
// Remove it later.
nodus.getNode('ada').removeClass('highlight');

Node visuals

Set any of these via an attributes object (on the element, in a rule, or in a class). All are optional.

nodus.getNode('a').setAttributes({
// Solid colour, or an array = equal-angle pie slices.
color: '#6366f1',
radius: 16,
// shape: circle | square | triangle | triangleLeft | triangleRight |
// diamond | pentagon | hexagon | star
shape: 'hexagon',
// Inner/outer strokes: { color, width, minVisibleSize }.
innerStroke: { color: '#1e293b', width: 1 },
outerStroke: { color: '#ffffff', width: 2 },
// Enable the default outline ring (use outerStroke above for a custom colour).
outline: true,
// A soft glow.
halo: { color: '#a5b4fc', width: 10 },
});

A pie node is just a color array — each colour fills an equal slice:

nodus.getNode('a').setAttributes({
color: ['#ef4444', '#f59e0b', '#10b981'], // three equal slices
});

Try it live — colour arrays rendered as pie slices:

Pie fills Open in new tab ↗

Icons, badges, and images sit on top of the node body:

nodus.getNode('a').setAttributes({
// An icon-font glyph.
icon: { content: '', font: 'FontAwesome', color: '#fff', scale: 0.6 },
// A positioned overlay badge:
// badges.{topLeft | topRight | bottomLeft | bottomRight}.text.content
badges: { topRight: { text: { content: '3' } } },
// An image drawn on the node.
image: { url: '/avatars/ada.png' },
});

Try it live — icons sitting on the node body with small overlay badges:

Icons & badges Open in new tab ↗

Here are every built-in shape and a range of pies, strokes, halos and labels, live — drag the nodes and zoom in:

Node shapes Open in new tab ↗

And here’s the soft halo glow on its own — drag the nodes to see it follow:

Halo glow Open in new tab ↗

Labels

The text attribute is the node label. It supports a position, a background, a secondary line, and wrapping. content may be a string or a function.

nodus.getNode('a').setAttributes({
text: {
content: 'Ada Lovelace',
color: '#0f172a',
position: 'bottom', // top | bottom | left | right | center
backgroundColor: 'inherit',
maxLineLength: 16, // wraps long labels; \n is honoured too
secondary: {
content: 'core team',
color: '#64748b',
},
},
});

Try it live — label positions, wrapping, a secondary line, and pill backgrounds:

Labels & positions Open in new tab ↗

Pulse and layer

pulse draws animated expanding rings — useful to draw the eye to a node. layer controls z-order (higher draws on top).

nodus.getNode('a').setAttributes({
pulse: {
startRatio: 1, endRatio: 2.5,
width: 3,
startColor: '#6366f1', endColor: 'rgba(99,102,241,0)',
interval: 1200, duration: 900,
},
layer: 2, // draw above neighbours
});

Try it live — animated pulse rings drawing the eye to a node:

Pulse Open in new tab ↗

Edge visuals

Edges carry their own attribute vocabulary. The shape object controls the arrowheads and line style.

nodus.getEdge('e1').setAttributes({
shape: {
// head/tail: arrow | open-arrow | square | circle | none
head: 'arrow',
tail: 'none',
style: 'dashed', // dashed | dotted | (omit for solid)
},
width: 2,
color: '#94a3b8',
halo: { color: '#e2e8f0', width: 6 },
});

Try it live — the arrow, open-arrow, square, circle and none extremities:

Edge extremities Open in new tab ↗

Curvature is automatic: multiple edges between the same two nodes fan apart into arcs, and an edge whose source === target becomes a self-loop — there’s no curvature value to set.

Edge labels rotate to follow the edge — a primary line above, an optional secondary below, with \n honoured:

nodus.getEdge('e1').setAttributes({
text: {
content: 'reports to',
secondary: { content: 'since 2024' },
},
});

When an edge’s source and target are the same node, Nodus renders it as a self-loop arc beside that node automatically.

Try it live — self-loops where source === target:

Self-loops Open in new tab ↗

Hover and selection states

Instead of wiring pointer events, define how elements look while hovered or selected once, globally, with nodus.styles. Nodus applies these states for you.

// Hover styling.
nodus.styles.setHoveredNodeAttributes({
outerStroke: { color: '#6366f1', width: 3 },
});
nodus.styles.setHoveredEdgeAttributes({
color: '#6366f1',
width: 3,
});
// Selection styling.
nodus.styles.setSelectedNodeAttributes({
color: '#4f46e5',
halo: { color: '#c7d2fe', width: 10 },
});
nodus.styles.setSelectedEdgeAttributes({
color: '#4f46e5',
width: 3,
});

You can also style the disabled state with setDisabledNodeAttributes / setDisabledEdgeAttributes, and apply a whole theme with setTheme(theme).

Try it live — hover to highlight and click to select, with these styles applied:

Hover & select Open in new tab ↗

Toggling visibility

nodus.styles can show or hide whole categories at once — handy for decluttering a dense graph:

nodus.styles.setEdgesVisibility(false); // hide all edges
nodus.styles.setNodeTextsVisibility(false); // hide node labels

Next steps