Build Draggable Node Connections with the Power-Link Library

Category: Chart & Graph , Javascript | December 21, 2025
AuthorTem-man
Last UpdateDecember 21, 2025
LicenseMIT
Views143 views
Build Draggable Node Connections with the Power-Link Library

Power-Link is a lightweight, framework-agnostic JavaScript library that creates draggable Bezier curve connections between DOM nodes.

It handles the visual drawing, mart snapping, interaction logic, and connection management needed for node-based interfaces like workflows, flowcharts, mindmaps, and more.

Features:

  • Framework Agnostic: Works with vanilla JavaScript, React, Vue, or any other framework.
  • Drag-and-Drop: Drag from one connection point to another to establish links between nodes.
  • Automatic Position Updates: Connections redraw automatically when nodes move.
  • Multiple Connection Points: Each node supports left-side and right-side connection dots, or both simultaneously.
  • Smart Snapping: Detects nearby connection points and snaps the cursor to them within a configurable distance.
  • Interactive Deletion: Hovering over a connection line reveals a delete button for easy removal.
  • Mouse Wheel Zoom: Zoom in and out of your node canvas using the mouse wheel for better detail control and overview navigation.
  • Pan with Left-Click Drag: Navigate large node graphs effortlessly by dragging the canvas with your left mouse button.
  • Initial View State Control: Set the initial viewport position and zoom level programmatically.
  • Node Info Configuration: Store additional metadata and custom information with each registered node.
  • Event Callbacks: Fires events when connections are created or removed.

Use Cases:

  • Workflow Builders: Create and visualize a sequence of steps or tasks, like in AI automation or data pipeline tools.
  • Mind Mapping Tools: Generate free-form thought diagrams with connectable nodes.
  • System Architecture Diagrams: Enable technical users to drag and drop servers, services, and databases to map out system design.
  • Custom Form Logic Builders: Build complex form UIs where answers branch into different paths based on user input.

How To Use It:

1. Install Power-Link with a package manager.

# Yarn
$ yarn add power-link
# NPM
$ npm install power-link
# PNPM
$ pnpm install power-link

2. Import the library into your project.

import Connector from "power-link";

3. Create a container element with your nodes inside it. Each node needs a unique ID and a reference to its DOM element.

I recommend setting your container to position: relative. The library calculates coordinates based on this container. If you skip this, the lines might render in the wrong place.

<div id="canvas-container">
  <!-- Nodes will be injected here -->
  <!-- Node 1: AI Processor -->
  <div id="node1" class="node" style="left: 100px; top: 150px;">
    <div class="node-header">
      <div class="node-icon">🤖</div>
      <span>AI Core</span>
    </div>
    <div class="node-content">Processing engine for complex tasks.</div>
  </div>
  <!-- Node 2: Database -->
  <div id="node2" class="node" style="left: 500px; top: 100px;">
    <div class="node-header">
      <div class="node-icon">💾</div>
      <span>Database</span>
    </div>
    <div class="node-content">Storage for persistent data records.</div>
  </div>
  <!-- Node 3: API Gateway -->
  <div id="node3" class="node" style="left: 500px; top: 300px;">
    <div class="node-header">
      <div class="node-icon">🌐</div>
      <span>API Gateway</span>
    </div>
    <div class="node-content">External interface for client requests.</div>
  </div>
  <!-- Node 4: Analytics -->
  <div id="node4" class="node" style="left: 900px; top: 200px;">
    <div class="node-header">
      <div class="node-icon">📊</div>
      <span>Analytics</span>
    </div>
    <div class="node-content">Real-time data visualization.</div>
  </div>
</div>

4. Create a new connector instance and register nodes as follows.

const container = document.getElementById('canvas-container');
const connector = new Connector({
  container: container,
  // more options here
});

5. All configuration options to customize the connector.

  • container (HTMLElement, required): The parent element that contains your nodes and serves as the canvas context.
  • lineColor (String, default: “#155BD4”): Sets the color for connection lines. Accepts any valid CSS color value.
  • lineWidth (Number, default: 2): Controls the stroke width of connection lines in pixels.
  • dotSize (Number, default: 12): Defines the diameter of connection dots in pixels.
  • dotColor (String, default: “#155BD4”): Sets the color for connection dots. Uses the same format as lineColor.
  • dotHoverScale (Number, default: 1.8): Scale factor when hovering over connection dots
  • deleteButtonSize (Number, default: 20): Controls the size of the delete button that appears on hover.
  • enableNodeDrag (Boolean, default: true): Determines whether nodes can be repositioned by dragging.
  • enableSnap (Boolean, default: true): Controls whether the connection cursor snaps to nearby connection points during drag operations.
  • snapDistance (Number, default: 20): Sets the pixel radius within which snapping activates. I’ve found 20 pixels works well in most layouts, but you might increase this for touch interfaces.
  • enableZoom (Boolean, default: true): Enable mouse wheel zoom functionality.
  • enablePan (Boolean, default: true): Enable canvas panning with left-click drag.
  • minZoom (Number, default: 0.1): Minimum zoom level (10%).
  • maxZoom (Number, default: 4): Maximum zoom level (400%).
  • zoomStep (Number, default: 0.1): Zoom step size per mouse wheel increment (10%).
  • dotPositions (Array/String, default: []): Controls which connection points appear on the node. ‘both’, [‘left’, ‘right’], [‘left’], [‘right’]
  • onConnect (Function, default: empty function): Callback that fires when a new connection is established. Receives a connection object with from, to, fromDot, and toDot properties.
  • onDisconnect (Function, default: empty function): Callback that fires when a connection is removed. Receives the same connection object structure.
  • onViewChange (Function): Callback triggered when the canvas view (zoom or pan) changes.
const container = document.getElementById('canvas-container');
const connector = new Connector({
  container: container,
  lineColor: "#155BD4",
  lineWidth: 2,
  dotSize: 12,
  dotColor: "#155BD4",
  dotHoverScale: 1.8,
  deleteButtonSize: 20,
  enableNodeDrag: false,
  snapDistance: 20,
  enableSnap: false, 
  enableZoom: false, 
  enablePan: false, 
  minZoom: 0.1,
  maxZoom: 4,
  zoomStep: 0.1,
  dotPositions: [],
  onConnect: (data) => addLog(`Connected: ${data.from} → ${data.to}`, 'success'),
  onDisconnect: (data) => addLog(`Disconnected: ${data.from} → ${data.to}`, 'warn'),
  onViewChange: (view) => {
    zoomDisplay.textContent = `Zoom: ${Math.round(view.scale * 100)}%`;
  }
});

6. API methods.

registerNode(id, element, options)

This method adds a node to the connector system and creates its connection points.

Parameters:

  • id (String): A unique identifier for the node. You’ll use this ID when creating programmatic connections.
  • element (HTMLElement): The DOM element that represents the node.
  • options (Object): Configuration object with a dotPositions property.

The dotPositions property accepts either a string or an array. You can pass “both” as a shorthand for both connection points, or use an array like ["left"], ["right"], or ["left", "right"] for more control.

connector.registerNode("myNode", element, {
  dotPositions: ["right"],
});

The method returns a node object that the library uses internally. You don’t need to store this return value unless you’re creating programmatic connections immediately.

createConnection(fromNode, toNode, fromDot, toDot, {silent=true/false})

Creates a connection between two nodes programmatically instead of through user interaction.

Parameters:

  • fromNode (Object): The source node object returned from registerNode.
  • toNode (Object): The target node object returned from registerNode.
  • fromDot (Object, optional): The specific connection dot to use on the source node.
  • toDot (Object, optional): The specific connection dot to use on the target node.
  • silent (Boolean, optional): Perform connection operations without triggering callbacks
const node1 = connector.nodes[0];
const node2 = connector.nodes[1];
connector.createConnection(node1, node2);

In my tests, omitting the dot parameters causes the library to use the default connection points. This works fine for simple cases but you’ll want to specify dots when nodes have multiple connection options.

disconnect(connectionId, {silent=true/false})

Removes one or all connections from the system.

Parameters:

  • connectionId (String, optional): The unique identifier for a specific connection. Omit this to remove all connections at once.
connector.disconnect();
connector.disconnect("connection-id");

getConnections()

Returns an array of all current connections with their metadata.

const connections = connector.getConnections();

Each connection object includes an id, from node ID, to node ID, fromDot position, and toDot position. This method is useful when you need to serialize the connection state for saving or transmission.

getNodeConnections(nodeId)

Retrieves all connections associated with a specific node, whether the node is the source or target.

Parameters:

  • nodeId (String): The unique identifier for the node.

This method is particularly useful when you need to update or validate connections for a single node without iterating through the entire connection list.

updateNodePosition(nodeId)

Manually triggers a position recalculation for a node and redraws its connections.

Parameters:

  • nodeId (String): The unique identifier for the node.

The library calls this automatically during drag operations, but you might need it if you move nodes programmatically through CSS transforms or position changes.

destroy({silent=true/false})

Removes all event listeners, clears the canvas, and cleans up internal references. Call this method before removing the connector from the DOM to prevent memory leaks.

connector.destroy();

setViewState(state)

Set the initial viewport state programmatically, including zoom level and pan position.

Parameters:

  • state (Object): An object containing scale, translateX, and translateY properties.

FAQs:

Q: Why don’t my connections appear when I register nodes?
A: The container element must have position: relative or position: absolute set in its CSS. The library creates an absolutely positioned canvas overlay that needs this positioning context to align correctly with your nodes. Also make sure that your node elements themselves use absolute positioning within the container.

Q: Can I use this library with dynamically created nodes?
A: Yes. Call registerNode() whenever you add a new node to the interface. The library doesn’t track the DOM automatically, so you’re responsible for registering nodes when they appear and calling destroy() or managing cleanup when they’re removed.
Q: How do I save and restore connections when the page reloads?

A: Use getConnections() to retrieve the current connection state as an array of objects. Serialize this array to JSON and save it to localStorage, a database, or your backend. When restoring, register all nodes first, then loop through your saved connections and call createConnection() for each one using the stored node IDs.

Q: What happens to connections when I move a node programmatically?
A: If you change a node’s position through JavaScript instead of the built-in drag functionality, call updateNodePosition(nodeId) after the position change. The library listens to its own drag events but doesn’t monitor external position changes.

Changelog:

v0.0.9 (12/21/2025)

  • added a new configuration option dotHoverScale that allows you to customize the scale factor when hovering over connection dots.

v0.0.8 (12/07/2025)

  • Added support for silent operations that allow you to perform connection operations without triggering callbacks.

v0.0.7 (11/29/2025)

  • Added Mouse Wheel Zoom,Pan with Left-Click Drag, Initial View State Control, Node Info Configuration and more

You Might Be Interested In:


Leave a Reply