⚠️ Experimental. Graph is not covered by the 1.0 stability guarantee — its layout, rendering, and options may change without a major-version bump. For production reporting, prefer the stable chart types.
The Graph chart renders networks of relationships — entity connections, agent networks, blockchain addresses. Instead of a single force simulation, it is a viewport over a headless graph model with three deterministic views: ego (who surrounds a node), path (how two nodes are connected), and cluster (the shape of the whole network). You never draw the full graph — you draw the answer to a query.
Ego view
Nodes snap to a regular grid sized for the neighborhood. The focused node is pinned to the center cell; every category (node group) owns a home corner or edge and fills cells from its far corner toward the center — so the deepest connections sit at the rim and first-ring ones end up next to the ego, and distance from the center still reads as degrees of separation. The layout is deterministic — same data, same picture. Nodes are monochrome with a category glyph; color belongs to the links, and the filled dark node marks the current center. Click any node to recenter on it: the chart queries the source for that node's neighborhood, and nodes present in both views travel to their new positions instead of being redrawn. Zoom out to switch to the cluster overview (semantic zoom).
Path view
"How are A and B connected?" — up to pathCount edge-disjoint routes, laid out left to right: columns are hops, each route gets its own row with a hop-count caption, and the shortest route is visually emphasized while the alternatives recede. Each endpoint's other connections fan out behind it, faded — showing that both nodes are well connected beyond these routes (disable with pathContext: false). Pathfinding is the data source's job (server-side on a real backend), because the client only holds the neighborhoods it has walked.
Cluster view
The whole network collapsed into communities (Louvain). Each meta-node is labelled by its most-connected member; size encodes the community size, link width the number of real edges between groups. Click a community to dive into the ego view of its top member; zoom in to dive back to the last ego view.
Example: warm-intro routing
A CRM-style scenario: the ego view is centered on a sales rep, and target leads — deliberately not connected to the rep — are marked with a star glyph. Clicking a lead answers the question that matters: who can introduce me? The onNodeClick callback switches to the path view with the warm-intro routes; clicking the rep restores the ego view. Category glyphs (person, company, partner org, ★ lead) carry the node semantics, so all the color stays on the relationship types.
Data source
Graph reads data through a source adapter with three async queries — the same contract a real graph backend would implement:
{
neighbors(id, { depth, types }) // → { nodes, links } — ego view
paths(a, b, { k }) // → { paths, nodes, links } — path view
aggregates() // → { communities, links } — cluster view
}
For static payloads, RareCharts.memorySource(data) simulates that backend over a full in-memory graph:
const dataSource = RareCharts.memorySource({ nodes, links });
new RareCharts.Graph('#chart', { dataSource, depth: 2 }).focus('some-node');
The option is named dataSource because source keeps its usual RareCharts meaning — the attribution line in the chart footer.
Everything the user walks through accumulates in a client-side model, so revisiting a node doesn't refetch it. Swapping memorySource for a real backend adapter changes nothing else.
Nodes
Each node requires only id. label and every other field are optional; when no label is supplied, Graph displays the id:
| Field | Type | Default | Description |
|---|---|---|---|
id |
string | — | Unique identifier (required) |
label |
string | id |
Display name |
group |
string | default |
Affects node color; can drive ego sectors via groupBy: 'group' |
size |
number | 1 |
Radius multiplier 0.6–3; read only with sizeBy: 'field' (nodes are uniform by default) |
color |
string | — | Explicit fill override. By default nodes are monochrome — color encodes link types only |
image |
string | — | Avatar URL, rendered inside the circle |
hidden |
boolean | false |
Keep the node out of the ego view without removing it from the data — for known-but-noisy entities. Runtime equivalent: hide(id) / show(id). The path view still draws routes through hidden nodes |
Links
Each link requires source and target node ids:
| Field | Type | Default | Description |
|---|---|---|---|
source |
string | — | Source node id (required) |
target |
string | — | Target node id (required) |
type |
string | 'default' |
Controls color, dash style, arrow, legend label — and ego sectors |
weight |
number | 0.5 |
0–1. Observable tie strength from your domain (deal count, transaction volume). Drives line thickness and the choice of a node's primary branch in the ego layout. strength is accepted as a legacy alias |
label |
string | — | Available in tooltipFormat, not rendered on the graph |
Note the division of labor: link weight is data you bring (observable ties), while node importance is computed — the model runs degree and betweenness centrality over everything loaded, so "who has the most connections" and "who do the paths run through" are answers, not inputs.
Link types
The type field on a link is resolved against the linkTypes map you pass in. Each entry defines how that type looks:
{
professional: { color: '#00aaff', dash: null, label: 'Professional' },
family: { color: '#00c97a', dash: null, label: 'Family' },
}
color— stroke color for the link line and arrowdash— SVGstroke-dasharraystring, ornullfor solidlabel— text shown in the legend
If a link type is not found in linkTypes, it falls back to t.muted (theme gray). Five presets are available via RareCharts.linkPresets:
| Preset | Types |
|---|---|
personal |
professional, family, friendship, investment, philanthropy, education |
knowledge |
partOf, causes, related, example, contradicts, prerequisite, extends |
org |
subsidiary, investment, board, partnership, acquisition, competitor |
tech |
depends, calls, dataFlow, inherits, optional |
causal |
causes, enables, blocks, correlates, weakens |
Options
| Option | Type | Default | Description |
|---|---|---|---|
view |
string | 'ego' |
Initial view: 'ego' | 'path' | 'cluster' |
dataSource |
object | — | Source adapter (see Data source). Optional if you call setData() |
depth |
number | 2 |
Ego neighborhood depth (rings) |
groupBy |
string | 'group' |
Ego sectors by node 'group' or by first-ring link 'type' |
sectorLabels |
boolean | false |
Caption the ego category corners |
hiddenNodes |
array | [] |
Node ids to keep out of the ego view (see the hidden node field) |
relationTypes |
array | — | Initial ego-view relation filter: only the listed types are traversed and drawn — an off-type tie between two visible nodes stays hidden. Links without a type match 'default'. Omit or pass an empty array to show every relation type |
interactiveLegend |
boolean | true |
Legend items filter relation types. A regular click isolates one type; Shift/Ctrl/Cmd-click toggles types in a multi-selection |
breadcrumbs |
boolean | true |
Show clickable semantic history after the user visits more than one view |
historyLimit |
number | 12 |
Maximum number of graph states retained by breadcrumb and back() navigation |
onNodeClick |
string | function | 'recenter' |
'recenter', a ({ node, event }) => … callback, or null |
pathCount |
number | 3 |
Max routes fetched by connect() |
pathContext |
boolean | true |
Fan the endpoints' other connections behind them in the path view, faded |
pathContextCount |
number | 8 |
Max context ties per endpoint |
semanticZoom |
boolean | true |
Zooming out of ego switches to the cluster view, and back |
height |
number | 520 |
Container height in px |
nodeRadius |
number | 22 |
Base node radius in px. Per-node size multiplies this |
nodeIcons |
object | false | built-in set | Glyphs inside nodes by group: { group: svgPath } (24×24 viewBox), merged over built-ins for person, company, fund, org, politics, education, crypto, lead, cluster. false renders plain circles |
sizeBy |
string | — | Uniform size by default. 'degree' computes size from connectivity in the accumulated model; 'field' reads the per-node size value |
zoom |
boolean | true |
+/−/reset buttons and drag-to-pan. The mouse wheel is left to page scroll |
maxNodes |
number | 'auto' | 'auto' |
Cap on rendered ego nodes. 'auto' derives it from the canvas size; first-ring and best-connected nodes win. Clicking +N more opens the omitted-node list, where any node can become the new focus |
draggable |
boolean | true |
Nodes can be hand-dragged to fine-tune the picture; positions persist until the next view change |
linkTypes |
object | { default: … } |
Type styling map or a preset |
tooltipFormat |
function | built-in | ({ node, links }) => html — custom tooltip renderer |
duration |
number | 500 |
Transition ms for view changes; respects reduced-motion |
Methods
All methods return the chart instance; fetching and rendering are async — whenReady() resolves when queued view changes are done.
const graph = new RareCharts.Graph('#chart', { dataSource, depth: 2 });
graph.focus('peter-thiel'); // ego view around a node
graph.focus('peter-thiel', {
types: ['investment', 'professional'], // filter the neighbors() query
});
graph.connect('peter-thiel', 'target'); // routes between two nodes
graph.overview(); // community overview
graph.setData({ nodes, links }); // static payload: memorySource + focus
graph.add({ links: [{ source: 'a', target: 'b', type: 'deal' }] });
// incremental: merge one news-sized payload
graph.hide('noisy-node'); // declutter; show(id) brings it back
graph.setRelationTypes(['investment']); // update the current ego filter
graph.clearRelationTypes(); // restore all relation types
graph.back(); // previous ego/path/cluster state
graph.clearHistory(); // keep only the current breadcrumb
await graph.whenReady();
add() is built for feed-driven graphs (a news item asserts a new tie): the minimal payload is a single link — endpoint nodes are created automatically, with the label falling back to the id.
Interaction
Recenter on click — clicking a node fetches its neighborhood and re-lays the view around it; shared nodes animate to their new positions so you keep your bearings while walking the graph. In the cluster view, clicking a community recenters on its most-connected member.
Focus + context on hover — hovering a node highlights its direct neighborhood and fades everything else, along with a tooltip listing connections by type.
Filter relations from the legend — click a relation type to isolate it; Shift/Ctrl/Cmd-click toggles types in a multi-selection. Filtering is applied to the neighbors() query and the client-side neighborhood. "Show all" clears the filter. Graphs whose links have no explicit type continue to work as a single default relation type.
Reveal omitted nodes — when the viewport cap removes nodes from the drawing, the underlined +N more note opens an accessible list. Selecting an item recenters the graph on it. Capacity omissions are separate from nodes hidden explicitly with hide().
Breadcrumb navigation — after more than one semantic view has been visited, Graph shows a clickable history of ego, path, and cluster states. Returning through a breadcrumb or back() restores the relation filter together with the view; literal pan and zoom are deliberately reset.
Zoom buttons — +/− buttons zoom, ⟲ resets, dragging the background pans. The mouse wheel deliberately stays with page scrolling.
Node dragging — drag any node to fine-tune the picture (no simulation fights back); hand-placed positions survive re-renders until the next view change.
Hide on right-click — right-click any node in the ego view to hide it (the focused center is exempt); a muted "N hidden · restore" control in the corner brings everything back. Same mechanism as the hidden field and hide(id)/show(id) — the node stays in the model and in path routes.
Semantic zoom — zooming far out of an ego view switches to the cluster overview; zooming into the overview returns to the last ego view. Disable with semanticZoom: false.
Minimal example
new RareCharts.Graph('#chart', {
linkTypes: RareCharts.linkPresets.personal,
depth: 2,
}).setData({ nodes, links });
Notes
The demos on this page run on a ~300-node synthetic network around Peter Thiel (generated deterministically — a stand-in for a graph database). The layouts are deterministic by design: rings, sectors, and route maps encode what is known about the data instead of asking a physics simulation to discover it. If a view still looks busy, reduce depth, filter link types, or let the cluster view do the summarizing.