Glowing Icon Node Tree Style
A static left-to-right relation-graph example that renders a dark neon tree with one reusable icon-and-label node slot. It also demonstrates CSS-based checked-state theming, a floating settings window for wheel and drag mode switching, and canvas image export.
Dark Neon Tree Viewer With Custom Icon Nodes
What This Example Builds
This example builds a static left-to-right tree viewer with a dark radial canvas and one reusable custom node template. The graph loads an inline hierarchy-like dataset, centers it, fits it into view, and renders every node as a glowing capsule that combines a LandPlotIcon with the node label. A floating utility window stays above the canvas so the user can read the description, open a settings panel, switch wheel and drag behavior, and export the current graph as an image. The most notable presentation detail is that line labels stay hidden until relation-graph marks a line as checked.
How the Data Is Organized
The data is declared inline inside initializeGraph() as one RGJsonData object with rootId, nodes, and lines. There is no fetch step and no preprocessing pipeline before setJsonData; the example only constructs the literal object and sends it directly to the graph instance.
The structure is still slightly richer than a plain tree. Most lines leave the base node, but several lines point back into the root, and another branch points into node 7 from H-7-1 and H-7-2. In a real product, the same shape could represent an org view with upstream and downstream reporting, a service dependency map with reverse callers, or a review workflow where one central item has both outgoing and incoming relationships.
How relation-graph Is Used
index.tsx wraps the example in RGProvider, which makes relation-graph hooks available to both the main graph component and the shared floating settings panel. Inside MyGraph.tsx, RelationGraph receives a focused graphOptions object: a tree layout, left-to-right direction, wide horizontal spacing, tight vertical spacing, circular node geometry, curved lines, left-right junction anchors, and transparent default node chrome so the custom slot and SCSS control the final appearance.
The example uses RGHooks.useGraphInstance() in two places. In MyGraph, the hook loads the inline dataset with setJsonData() and then calls moveToCenter() and zoomToFit() after mount. In CanvasSettingsPanel, the same hook drives runtime behavior: it updates wheelEventAction and dragEventAction with setOptions(), prepares the canvas for export with prepareForImageGeneration(), reads the current background with getOptions(), and restores the graph after capture with restoreAfterImageGeneration(). RGHooks.useGraphStore() is used there as well so the panel reflects the current interaction settings.
The only custom graph slot in local code is RGSlotOnNode. It replaces the default node body with one consistent React fragment that renders an icon and text for every node. There are no local line slots, canvas slots, viewport slots, explicit graph event handlers, or editing APIs. Most of the visual customization happens through my-relation-graph.scss, which overrides relation-graph classes such as .rg-map, .rg-node-peel, .rg-line-peel, .rg-node-checked, and .rg-line-checked.
Key Interactions
- The graph initializes on mount, loads its static dataset, moves the root cluster into the center, and zooms to fit the full structure.
- The floating description window can be dragged by its title bar and minimized when the settings panel is closed.
- The gear button opens a settings overlay, and clicking the translucent backdrop closes it again.
- The settings overlay switches
wheelEventActionbetweenscroll,zoom, andnone. - The same overlay switches
dragEventActionbetweenselection,move, andnone. - The
Download Imageaction prepares the graph canvas, captures it withmodern-screenshot, downloads the blob, and restores the graph state. - The stylesheet defines checked-state feedback: checked nodes switch to a green glow, and checked lines reveal their labels with a green background.
Key Code Fragments
This fragment shows that the example removes the library’s default node chrome so the slot markup and SCSS become the visible node renderer.
const graphOptions: RGOptions = {
backgroundColor: '#004466',
defaultLineColor: 'rgba(255, 255, 255, 0.6)',
defaultNodeColor: 'transparent',
defaultNodeBorderWidth: 0,
defaultNodeBorderColor: 'transparent',
defaultNodeShape: RGNodeShape.circle,
defaultLineShape: RGLineShape.StandardCurve,
defaultJunctionPoint: RGJunctionPoint.lr,
};
This fragment shows the tree layout settings that create the left-to-right hierarchy view.
layout: {
layoutName: 'tree',
from: 'left',
treeNodeGapH: 140,
treeNodeGapV: 10
}
This fragment shows that the dataset mixes outgoing and incoming relations around the root instead of modeling a strictly one-direction tree.
lines: [
{ id: 'l1', from: 'base', to: '1', text: 'Line X01' },
{ id: 'l5', from: '5', to: 'base', text: 'Incoming Line X05' },
{ id: 'l6', from: '6', to: 'base', text: 'Incoming Line X06' },
{ id: 'l1-01', to: '7', from: 'H-7-1', text: 'Line X7-1' },
{ id: 'l1-07', to: 'H-7-21', from: 'H-7-211', text: 'Line X7-21-1' }
]
This fragment shows the single reusable node slot that gives every node the same icon-and-label body.
<RGSlotOnNode>
{(nodeSlotProps) => {
const { node } = nodeSlotProps;
return (
<div className="my-icon-node gap-2 flex rounded-full place-items-center justify-center">
<LandPlotIcon size={10} />
{node.text}
</div>
);
}}
</RGSlotOnNode>
This fragment shows how the export flow uses relation-graph instance APIs before and after the screenshot step.
const canvasDom = await graphInstance.prepareForImageGeneration();
let graphBackgroundColor = graphInstance.getOptions().backgroundColor;
if (!graphBackgroundColor || graphBackgroundColor === 'transparent') {
graphBackgroundColor = '#ffffff';
}
const imageBlob = await domToImageByModernScreenshot(canvasDom, {
backgroundColor: graphBackgroundColor
});
await graphInstance.restoreAfterImageGeneration();
This fragment shows the selection-aware line styling: labels are hidden by default and only appear in the checked state.
.rg-line-peel {
.rg-line-label {
display: none;
}
}
.rg-line-peel.rg-line-checked {
.rg-line-label {
display: block;
background-color: #028c02;
}
}
What Makes This Example Distinct
According to the prepared comparison data, this example is not just another node-style demo. Its strongest differentiator is focus: it uses one consistent icon-and-text node family instead of comparing many slot variants, and it coordinates that node treatment with a dark radial canvas, cyan glow styling, green checked-state feedback, and hidden-until-checked edge labels.
That makes it meaningfully different from nearby examples. Compared with node-style2, it moves from CSS-only skinning of the default node renderer to full slot-based node rendering while keeping the same static inspection pattern. Compared with node-style3, it trades force-layout motion and metadata-driven icon variety for a calmer left-to-right hierarchy view with stronger checked-state line feedback. Compared with node-style, it is less about showcasing many slot templates and more about refining one repeatable visual system that can be reused across a whole graph.
The comparison data also warns against overstating uniqueness. The floating settings window and image export flow come from shared demo scaffolding used in neighboring examples, so the real value of node-style4 is the focused combination of a single custom node slot, transparent built-in node chrome, and selection-aware line-label reveal inside one cohesive theme.
Where Else This Pattern Applies
This pattern transfers well to dashboards that need a polished read-only relationship view without building an editor. Examples include team or org viewers, system dependency maps, asset lineage trees, approval chains, and topology overviews where one node family should stay visually consistent across the whole graph.
It also works well for cases where relationship metadata should stay quiet until the user focuses on a specific edge. The checked-state label reveal can reduce clutter in dense graphs while still exposing link names when needed. The floating settings and export panel can be reused for internal tools that need quick screenshot capture or temporary interaction-mode switches without adding permanent toolbar chrome to the canvas itself.