Tree - Performance Mode Test
This example builds a full-screen stress playground for relation-graph's built-in tree layout in performance mode. It regenerates synthetic hierarchies at multiple size presets, keeps custom employee-card nodes and a minimap active, and lets users retune layout and connector presentation on the live graph.
Stress-Testing Tree Layout in Performance Mode with Live Runtime Controls
What This Example Builds
This example builds a full-screen tree-layout stress playground rather than a business-specific viewer. It renders a synthetic hierarchy with custom employee-card nodes, colored labeled connectors, a floating control window, and a bottom-right mini view.
Users can switch between six dataset-size presets, retune tree direction and spacing, restyle live connectors, change canvas wheel and drag behavior, and export an image. The main point of the example is that it keeps a richer presentation layer active while exercising relation-graph’s built-in tree layout in performanceMode.
How the Data Is Organized
The data starts as one inline RGJsonData seed with rootId: 'a', a flat nodes array, and a flat lines array. That seed already forms a tree, and generateTestJsonData(testDataSize) clones it before adding more descendants.
The preprocessing step is important here. For every node whose id contains -, the generator appends more child nodes and edges. When testDataSize is above 30, it switches to a two-level square-root expansion; otherwise it appends one extra level from 1 through testDataSize. After expansion, every node gets a random color and avatar URL, and every line gets a generated id, from > to text, and a random color.
In a real application, the same structure could stand in for large org charts, category trees, document hierarchies, access-control trees, or any generated hierarchy used to test layout readability before connecting to real backend data.
How relation-graph Is Used
The example is wrapped in RGProvider, and MyGraph uses RGHooks.useGraphInstance() as its main runtime control surface. The base RGOptions enable rectangular nodes, orthogonal connectors, a built-in tree layout, top-right toolbar placement, debug defaults, and performanceMode: true.
The graph logic is deliberately split into three runtime paths. A dataset-size change regenerates JSON data, stops auto layout, clears the graph, reloads the data through setJsonData(...), recenters the viewport, fits the graph, and then reapplies line styling. A layout change updates the live layout block with updateOptions(...), calls doLayout(), and refits the viewport without rebuilding the dataset. A line-style change loops over getLines() and applies updateLine(...) in place.
Slots are central to the presentation. RGSlotOnNode renders MyNodeSlot, which turns every node into a compact employee-style card that reads node.data.pic and node.text. RGSlotOnView mounts RGMiniView as a persistent overview widget. Styling in my-relation-graph.scss removes the built-in node skin, drives node borders from --rg-node-color, and makes line labels inherit their connector color.
The floating DraggableWindow is a shared utility, but it matters to this example because it adds the dataset selector, the reusable MyTreeGraphOptions panel, a settings overlay that calls setOptions(...) on the live graph, and an image-export flow based on prepareForImageGeneration() plus restoreAfterImageGeneration().
Key Interactions
The primary interaction is scale switching. Clicking one of the six preset buttons changes testDataSize, which rebuilds the synthetic tree and reloads the graph.
The second major interaction is runtime layout tuning. Users can switch tree direction between left, right, top, and bottom, change horizontal and vertical node gaps, and immediately rerun the built-in tree layout on the current graph.
The third major interaction is live connector tuning. The same floating panel changes line shape, orthogonal radius, junction points, and line text offsets by mutating the currently mounted lines instead of rebuilding the graph.
The helper window itself is interactive: it can be dragged, minimized, expanded into a canvas-settings overlay, switched between wheel and drag modes, and used to download a rendered image. Node and line clicks are present only for console inspection and do not change the graph.
Key Code Fragments
This fragment shows that the example turns on the built-in tree layout with fixed rectangular nodes and performanceMode.
const graphOptions: RGOptions = {
defaultNodeShape: RGNodeShape.rect,
defaultNodeWidth: 150,
defaultNodeHeight: 60,
defaultLineShape: RGLineShape.StandardOrthogonal,
defaultLineWidth: 3,
layout: {
layoutName: 'tree',
treeNodeGapH: 400,
treeNodeGapV: 10
},
performanceMode: true
};
This fragment proves that the dataset is cloned and expanded before it is sent to relation-graph.
const data = JSON.parse(JSON.stringify(jsonData));
const newNodes = [];
const newLines = [];
for (const node of data.nodes) {
if (node.id.includes('-')) {
if (testDataSize > 30) {
const level1 = Math.sqrt(testDataSize);
for (let a = 1; a <= level1; a++) {
const aNodeId = node.id + '-' + a;
newNodes.push({ id: aNodeId, text: aNodeId });
newLines.push({ from: node.id, to: aNodeId });
This fragment shows the reset-and-reload sequence that runs when the size preset changes.
const initializeGraph = async () => {
const myJsonData = generateTestJsonData(testDataSize);
graphInstance.stopAutoLayout();
graphInstance.clearGraph();
await graphInstance.sleep(200);
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
updateData();
};
This fragment shows that layout direction and spacing are updated on the live graph through updateOptions(...) and doLayout().
graphInstance.updateOptions({
layout: {
layoutName: 'tree',
from: myTreeGraphOptions.layoutFrom,
treeNodeGapH: myTreeGraphOptions.rangeHorizontal,
treeNodeGapV: myTreeGraphOptions.rangeVertical
}
});
await graphInstance.doLayout();
graphInstance.moveToCenter();
graphInstance.zoomToFit();
This fragment shows that connector style changes are applied by iterating over mounted lines instead of rebuilding data.
graphInstance.getLines().forEach((line) => {
graphInstance.updateLine(line, {
lineShape: myTreeGraphOptions.lineShape,
lineRadius: myTreeGraphOptions.lineRadius,
fromJunctionPoint: myTreeGraphOptions.junctionPoint,
toJunctionPoint: myTreeGraphOptions.junctionPoint,
textOffsetX: myTreeGraphOptions.textOffsetX,
textOffsetY: myTreeGraphOptions.textOffsetY,
placeText: (myTreeGraphOptions.lineShape === RGLineShape.StandardOrthogonal) ? 'end' : undefined
});
});
This fragment shows the slot-based presentation layer: a mini view in the viewport and a custom node renderer for every tree node.
<RGSlotOnView>
<RGMiniView width="300px" height="150px" position="br" />
</RGSlotOnView>
<RGSlotOnNode>
{(nodeSlotProps: RGNodeSlotProps) => {
return <MyNodeSlot node={nodeSlotProps.node} />;
}}
</RGSlotOnNode>
What Makes This Example Distinct
The comparison data describes this example as a built-in tree stress lab, not as an external-layout integration or a graph editor. Its strongest rare combination is performanceMode, six synthetic scale presets, live tree-direction and gap controls, in-place orthogonal-line tuning, a custom employee-card node slot, and RGMiniView on one full-screen canvas.
Compared with performance-test-force-layout, it puts the emphasis on hierarchical readability and tree-specific presentation controls rather than force coefficients or solver duration. Compared with use-dagre-layout and use-dagre-layout-2, it is a better starting point when the requirement is to push relation-graph’s own tree layout under changing scale, not to bring in an external coordinate engine.
The comparison data also highlights a less common pairing: the example keeps custom slot-rendered cards, randomized per-node and per-line coloring, and color-synchronized line labels active while the hierarchy scales into performance-oriented sizes. Compared with node and layout-center, that makes slots and live style mutation part of a large-tree workload experiment instead of a small static styling showcase.
Where Else This Pattern Applies
This pattern applies to prelaunch scale checks for org charts, role hierarchies, curriculum trees, content taxonomies, and document maps where teams need to understand how a richer tree presentation behaves before real production data is connected.
It also transfers to internal tooling and QA workflows. The same separation between data regeneration, relayout-only updates, and line-only mutations is useful when a team wants a controlled sandbox for testing spacing presets, label placement, connector routing, export behavior, or slot-rendering cost on large hierarchies.