Skip to content
This is the alpha v4 version website. Looking for the v3 documentation?

Style the graph

In Part 2, you colored nodes by cluster and sized them by score. But the mapping was baked into the graph-building code. In many cases, you will want your graphology instance to be more data-focused, and decoupled from the actual visual variables.

In this step, you will move that visual logic into sigma’s styles system, which is more declarative, and enables conditional rules.

Store raw data, style separately

Instead of computing color and size when building the graph, store the raw attributes and let sigma’s styles do the mapping.

Update your index.ts. First, change the graph-building loop to only store raw data:

for (const node of dataset.nodes) {
graph.addNode(node.key, {
label: node.label,
x: node.x,
y: node.y,
// Store raw data instead of visual properties
cluster: node.cluster,
score: node.score,
});
}

To allow sigma to properly set nodes colors and sizes, we need to extract the clusters colors and the score extreme values:

// Extract cluster colors from data:
const clusterColors = Object.fromEntries(dataset.clusters.map((c) => [c.key, c.color]));
// Extract extreme score values from graph:
const { minScore, maxScore } = dataset.nodes.reduce(
({ minScore, maxScore }, { score }) => ({
minScore: Math.min(minScore, score),
maxScore: Math.max(maxScore, score),
}),
{ minScore: Infinity, maxScore: -Infinity },
);

Note that the graphology-metrics exports a nodeExtent function that makes it much simpler to extract extreme values.

Then, you will need to pass a styles option when creating the sigma instance. We start from DEFAULT_STYLES.nodes to keep the built-in hover behaviors (label visibility, depth layers, backdrops), then override color and size.

import { DEFAULT_STYLES } from "sigma/types";
const renderer = new Sigma(graph, document.getElementById("container")!, {
styles: {
nodes: [
DEFAULT_STYLES.nodes,
{
color: { attribute: "cluster", dict: clusterColors, defaultValue: "#999" },
size: { attribute: "score", min: 10, max: 50, minValue: minScore, maxValue: maxScore },
label: { attribute: "label" },
},
],
edges: [DEFAULT_STYLES.edges, { color: "#ccc", size: 5 }],
},
});

The result looks the same, but now the styling is declared in one place. The size binding maps the score attribute to a 10–50 range automatically.

Style value types

The styles system supports several value types:

  • Literal: color: "#ccc" (same value for all elements)
  • Attribute binding: color: { attribute: "color" } (reads from graph data)
  • Numerical binding: size: { attribute: "score", min: 10, max: 50 } (maps a numeric range)
  • Categorical binding: maps discrete values via a dictionary

For the full list, see the Style value types reference.

Full code and result

import Graph from "graphology";
import Sigma from "sigma";
import { DEFAULT_STYLES } from "sigma/types";
interface Dataset {
nodes: { key: string; label: string; tag: string; cluster: string; x: number; y: number; score: number }[];
edges: [string, string][];
clusters: { key: string; color: string; clusterLabel: string }[];
}
const response = await fetch("/data/wikipedia.json");
const dataset: Dataset = await response.json();
const graph = new Graph();
for (const node of dataset.nodes) {
graph.addNode(node.key, {
label: node.label,
x: node.x,
y: node.y,
cluster: node.cluster,
score: node.score,
});
}
for (const [source, target] of dataset.edges) {
if (graph.hasNode(source) && graph.hasNode(target) && !graph.hasEdge(source, target)) {
graph.addEdge(source, target);
}
}
const clusterColors = Object.fromEntries(dataset.clusters.map((c) => [c.key, c.color]));
const { minScore, maxScore } = dataset.nodes.reduce(
({ minScore, maxScore }, { score }) => ({
minScore: Math.min(minScore, score),
maxScore: Math.max(maxScore, score),
}),
{ minScore: Infinity, maxScore: -Infinity },
);
const renderer = new Sigma(graph, document.getElementById("sigma-container") as HTMLElement, {
styles: {
nodes: [
DEFAULT_STYLES.nodes,
{
color: {
attribute: "cluster",
dict: clusterColors,
defaultValue: "#999",
},
size: { attribute: "score", min: 10, max: 50, minValue: minScore, maxValue: maxScore },
label: { attribute: "label" },
},
],
edges: [DEFAULT_STYLES.edges, { color: "#ccc", size: 5 }],
},
});

Next steps

The graph now has declarative styles with a hover effect. In Part 4: Add interactivity, you will add neighbor highlighting and a search bar to build a fully interactive explorer.