JavaScript is required

Force Layout Isolated Nodes with Hidden Helper Lines

This example shows how to keep isolated or otherwise disconnected nodes present in a relation-graph force layout. It derives a root from the first node, adds hidden helper lines before loading, and then centers and fits the resulting graph in a full-height viewer.

Keeping Isolated Nodes Visible in a Force Layout

What This Example Builds

This example builds a full-height relation-graph viewer for a sparse network that contains both small connected clusters and nodes that would otherwise be disconnected. The visible result is a force-driven canvas with uniform circular nodes, a few dark straight links, and many standalone-looking nodes that still remain part of the loaded graph. The main point is not domain modeling or editing. It is the preprocessing pattern that keeps isolated nodes present in a force layout without drawing the extra structural edges on screen.

How the Data Is Organized

The graph data is declared inline as one RGJsonData object inside initializeGraph(). It starts with 20 nodes and 5 visible lines, then the code performs one preprocessing pass before setJsonData(...) runs: it derives rootId from the first node in the array, assigns that value back to the dataset, and appends one hidden line from that root to every other node by using generated l-hidden-* ids and opacity: 0.

That means the runtime graph is more connected than the visible graph. In a real application, the same structure could represent people with no current collaboration links, devices with incomplete topology data, or catalog entities that should stay visible even when explicit relationships are missing.

How relation-graph Is Used

index.tsx wraps the example in RGProvider, and MyGraph.tsx retrieves the mounted graph instance through RGHooks.useGraphInstance(). The graph options define the example’s whole visual behavior: border junction points, circular nodes, straight lines, 60 by 60 default node size, dark gray visible edges, and the built-in force layout with force_node_repulsion: 0.5.

The example does not use slots, editing APIs, or custom subcomponents beyond the provider and the main RelationGraph canvas. Its runtime flow is deliberately small: a mount-only useEffect(...) calls initializeGraph(), the data is augmented in memory, then the graph instance runs setJsonData(...), moveToCenter(), and zoomToFit(). The local SCSS file is only a scaffold of empty selectors, and MyGraph.tsx does not import it, so the visible presentation comes primarily from graph options and the inline height: '100vh' container.

Key Interactions

The first interaction is automatic rather than user-triggered: when the component mounts, the graph loads, centers itself, and fits to the viewport. After that, the example exposes node-click and line-click handlers, but both are inspection hooks only. They log the clicked object to the console and do not change the graph, open panels, or start editing.

Key Code Fragments

This options block shows that the example is explicitly a force-layout viewer with circular nodes and straight dark-gray visible links.

const graphOptions: RGOptions = {
    debug: false,
    defaultJunctionPoint: RGJunctionPoint.border,
    defaultNodeShape: RGNodeShape.circle,
    defaultLineShape: RGLineShape.StandardStraight,
    defaultNodeWidth: 60,
    defaultNodeHeight: 60,
    defaultLineWidth: 2,
    defaultLineColor: '#333333',
    layout: {
        layoutName: 'force',
        force_node_repulsion: 0.5
    }
};

This preprocessing step is the core workaround: it derives a root and injects invisible helper lines before the dataset is loaded.

const rootId = myJsonData.nodes[0].id;
myJsonData.rootId = rootId;

myJsonData.nodes.forEach(n => {
    if (n.id !== rootId) {
        myJsonData.lines.push({
            id: `l-hidden-${n.id}`,
            from: rootId,
            to: n.id,
            opacity: 0
        });
    }
});

This fragment shows the minimal graph-instance bootstrap that turns the prepared data into the initial viewport state.

await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();

This wrapper is what makes the hook-based graph instance available to MyGraph.

const Demo = () => {
    return (
        <RGProvider>
            <MyGraph />
        </RGProvider>
    );
};

What Makes This Example Distinct

According to the prepared comparison data, this example is distinctive because it applies the hidden-helper-edge workaround specifically to relation-graph’s built-in force layout. Its closest siblings, show-single-nodes and show-single-nodes3, use the same root-derivation and invisible-edge strategy, but they frame it around circle-layout variants instead of force behavior. That makes this example the stronger starting point when disconnected-looking nodes still need to survive inside a force-driven scene.

The comparison data also separates it from broader fragmented-graph examples such as multi-group-2. That example renders disconnected groups more directly, while this one rewrites the payload to manufacture an invisible connected backbone before load. Compared with advanced-line-usage, the emphasis is also different: here the extra lines are structural and hidden, not visible line-style features. The rare combination is a small inline dataset, log-only click handlers, circular 60 by 60 nodes, straight dark-gray visible edges, and a preprocessing pass that makes sparse data workable in a force layout.

Where Else This Pattern Applies

This pattern transfers well to graph views where incomplete relationship data should not cause records to disappear from the layout. Examples include employee maps that must still show unassigned staff, infrastructure graphs with orphan devices, product or customer graphs with partially imported links, and analysis dashboards that need every candidate entity on screen before relationship cleanup is finished.

It also works as an extension point for richer viewers. The hidden helper lines can stay as an internal loading tactic while visible links, filtering, detail panels, or selection styling are added later on top of the same preprocessing step.