Skip to main content
Version: 4.3

Connecting Elements

A diagram is elements and the relationships between them. In @joint/react, a connection is just another cell, a link, with type: 'link'.

import {
  GraphProvider,
  Paper,
  HTMLHost,
  linkRoutingOrthogonal,
  type ElementPort,
} from '@joint/react';
import '@joint/react/styles.css';
import './example.css';

type NodeData = { label: string; description: string; icon: string; color: string };

const ports: Record<string, ElementPort> = {
  in: { cx: 0, cy: '50%', width: 10, height: 10, passive: true },
  out: { cx: '100%', cy: '50%', width: 10, height: 10 },
};

const node = (id: string, x: number, y: number, data: NodeData) => ({
  id,
  type: 'element' as const,
  position: { x, y },
  size: { width: 160, height: 52 },
  portMap: ports,
  data,
});

// Links connect a source port to a target port.
const link = (id: string, source: string, target: string) => ({
  id,
  type: 'link' as const,
  source: { id: source, port: 'out' },
  target: { id: target, port: 'in' },
});

const initialCells = [
  node('trigger', 40, 40, { label: 'New Ticket', description: 'Trigger', icon: 'âš¡', color: '#f59e0b' }),
  node('classify', 40, 170, { label: 'Classify', description: 'AI Agent', icon: '🤖', color: '#3b82f6' }),
  node('check', 300, 105, { label: 'Is Urgent?', description: 'Condition', icon: 'â—†', color: '#a855f7' }),
  node('reply', 300, 240, { label: 'Send Reply', description: 'Action', icon: 'â–¶', color: '#22c55e' }),
  link('trigger→check', 'trigger', 'check'),
  link('classify→check', 'classify', 'check'),
  link('check→reply', 'check', 'reply'),
];

function NodeCard({ label, description, icon, color }: NodeData) {
  return (
    <HTMLHost useModelGeometry className="node">
      <span className="node__icon" style={{ color }}>
        {icon}
      </span>
      <div>
        <div className="node__label">{label}</div>
        <div className="node__desc">{description}</div>
      </div>
    </HTMLHost>
  );
}

export default function App() {
  return (
    <GraphProvider initialCells={initialCells}>
      <Paper renderElement={NodeCard} linkRouting={linkRoutingOrthogonal()} className="canvas" />
    </GraphProvider>
  );
}


Drag any element and the links re-route to follow. This is the flow the final demo is built around.

Linking two elements​

A link references its endpoints by id, optionally targeting a specific port:

{
id: 'l1',
type: 'link',
source: { id: 'trigger', port: 'out' }, // leave out `port` to connect the whole element
target: { id: 'check', port: 'in' },
}

The port: 'out' / port: 'in' values are the port names from the portMap you defined in Custom Elements. That is how a link attaches to a specific dot instead of the whole element.

Links live in the same initialCells array as elements, so they are part of the same data model: add, remove, or rewire them like any other cell.

Styling the line​

The style field controls the line. Every field is optional:

style: {
color: '#94a3b8', // stroke color (any CSS color or variable)
width: 2, // stroke width in px
targetMarker: 'arrow', // arrowhead at the target end
dasharray: '5,4', // dashes
}
FieldWhat it does
colorLine stroke color.
widthLine thickness in px.
targetMarker / sourceMarkerEnd decoration: 'arrow', 'none', or a named marker.
dasharrayDash pattern, e.g. '5,4'.

For richer arrowheads, @joint/react exports marker builders: linkMarkerArrow, linkMarkerDiamond, linkMarkerCircle, and more:

import { linkMarkerDiamond } from '@joint/react';

style: { color: '#64748b', width: 2, targetMarker: linkMarkerDiamond() }

Routing​

By default a link draws a straight line. The linkRouting prop on Paper sets how every link finds its path. The example uses orthogonal (right-angle) routing, the same look as the final demo:

import { linkRoutingOrthogonal } from '@joint/react';

<Paper renderElement={NodeCard} linkRouting={linkRoutingOrthogonal()} />

Three presets come built in:

  • linkRoutingStraight(): a direct line (the default).
  • linkRoutingOrthogonal(): horizontal/vertical segments, great for flowcharts.
  • linkRoutingSmooth(): curved connections.
Labels on links

Add text to a link with the labelMap field, a map of named labels, each at least a text:

{
id: 'l3',
type: 'link',
source: { id: 'check', port: 'out' },
target: { id: 'reply', port: 'in' },
labelMap: { decision: { text: 'yes' } },
}

Validating connections​

When the user drags to create a link, Paper checks the connection before committing it. Sensible rules apply out of the box, with no configuration:

  • No self-loops: an element cannot link to itself.
  • No duplicate links: one link per source → target direction, so a second trigger → check is rejected while the reverse check → trigger is still allowed.
  • No link-to-link connections.
  • A link may attach to the element body only when the element has no ports; otherwise it must land on a port.

To change the rules, pass validateConnection to Paper, either as an options object or your own callback:

<Paper validateConnection={{ allowSelfLoops: true }} />
<Paper validateConnection={({ target }) => target.port === 'in'} />

The full rule set and the callback context are in Validating connections.

For everything links can do (custom markers, anchors, custom link rendering), see Links.

You can draw any graph now. Let's make the canvas respond to the user.