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
- Per element — set attributes directly:
node.setAttributes({ color: 'red' }). - Rules —
nodus.styles.addNodeRule(...)applies attributes across many elements, and can compute values from each element. - Classes —
nodus.styles.createClass(...)defines a named look you toggle withelement.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:
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:
Here are every built-in shape and a range of pies, strokes, halos and labels, live — drag the nodes and zoom in:
And here’s the soft halo glow on its own — drag the nodes to see it follow:
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:
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:
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:
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:
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:
Toggling visibility
nodus.styles can show or hide whole categories at once — handy for decluttering
a dense graph:
nodus.styles.setEdgesVisibility(false); // hide all edgesnodus.styles.setNodeTextsVisibility(false); // hide node labelsNext steps
- Drive selection and hover with Handling Events.
- Frame the styled graph with Controlling the Camera.
- Add interactive tools in Interaction Tools.