Skip to main content
Version: 4.3

Ports

In @joint/react, declarative ports live on the element record's portMap.

A port definition answers three questions:

  • where does this connection point sit on the element (cx and cy)
  • what does it look like (width, height, color, outline, shape)
  • how should it behave (passive and label properties)

For most React diagrams, portMap is the default port API. It keeps ports in the same declarative data model as the rest of the graph, while @joint/react converts that data into native JointJS ports behind the scenes.

This chapter focuses on the preferred declarative port API: portMap and portStyle.

Sugar over native ports

portMap is a convenience layer over native JointJS ports: @joint/react converts each entry into a groups / items pair internally. Reach for native ports when you need parametric JointJS layouts like ellipseSpread or your own custom layout, generate ports programmatically per element, or mirror an existing native JointJS configuration.

Here is a complete example; the rest of the chapter breaks it down.

This example uses portMap for declarative port definitions, portStyle for shared defaults, and passive: true on the input port. The link connects through the port IDs.

portMap​

portMap is a record of port definitions keyed by port ID:

portMap: {
in: { cx: 0, cy: 'calc(0.5 * h)', passive: true },
out: { cx: 'calc(w)', cy: 'calc(0.5 * h)' },
}

Those keys are important because links refer to them directly:

source: { id: 'source', port: 'out' },
target: { id: 'review', port: 'in' },

Use stable port IDs like in, out, top, bottom, or error. Those IDs become part of your graph data and link endpoints.

warning

Setting both portMap and native ports on the same element throws. Pick one representation and stick to it.

Positioning with cx and cy​

Every declarative port needs cx and cy.

portMap: {
left: { cx: 0, cy: 'calc(0.5 * h)' },
right: { cx: 'calc(w)', cy: 'calc(0.5 * h)' },
top: { cx: 'calc(0.5 * w)', cy: 0 },
bottom: { cx: 'calc(0.5 * w)', cy: 'calc(h)' },
}

These values are absolute positions within the element's bounding box:

  • numbers like 0, 12, or 40 place the port at a fixed offset
  • expressions like calc(w), calc(0.5 * h), or calc(w - 8) position ports relative to the current element size

calc() expressions are the usual choice because they keep ports aligned even when the element size changes.

Shape and visual styling​

Each port can define its own size and appearance:

portMap: {
out: {
cx: 'calc(w)',
cy: 'calc(0.5 * h)',
width: 16,
height: 16,
color: '#2563eb',
outline: 'white',
outlineWidth: 2,
shape: 'ellipse',
className: 'my-port',
},
}

The main visual fields are:

  • width and height for the rendered size
  • color for the fill
  • outline and outlineWidth for the stroke
  • shape for the port body
  • className for CSS utilities or custom selectors

shape supports three kinds of values:

  • 'ellipse'
  • 'rect'
  • any SVG path string, which is treated as the port body's d attribute
shape: 'M -8 0 L 0 -8 L 8 0 L 0 8 Z'

Ports can also be themed with CSS variables such as --jj-port-color, --jj-port-border-color, --jj-port-border-width, and --jj-port-label-color. See Theming for the full --jj-port-* surface.

The preview below shows three nodes with different port shapes (ellipse, rect, and a custom diamond path):

Shared defaults with portStyle​

Use portStyle when many ports on the same element should share the same defaults:

portStyle: {
width: 14,
height: 14,
color: '#64748b',
outline: 'white',
outlineWidth: 2,
labelColor: '#475569',
},
portMap: {
in: { cx: 0, cy: 'calc(0.5 * h)', passive: true },
out: { cx: 'calc(w)', cy: 'calc(0.5 * h)', color: '#2563eb' },
}

Precedence is simple:

  • portStyle provides defaults for every port in portMap
  • the individual port entry overrides any overlapping field (shallow merge: nested objects are replaced, not merged)

Use portStyle for shared size, color, outline, and label defaults. That keeps portMap focused on the differences between ports.

passive vs interactive ports​

By default, a port is interactive and can initiate link creation.

out: {
cx: 'calc(w)',
cy: 'calc(0.5 * h)',
}

Set passive: true when the port should act as a target-only magnet:

in: {
cx: 0,
cy: 'calc(0.5 * h)',
passive: true,
}

Use passive for input ports that should accept links but not start them.

passive only controls magnet initiation on that specific port. If your diagram needs stricter source/target rules, enforce them with Paper's validateConnection callback rather than with port data alone.

Port labels​

Ports can render their own labels without extra React code:

portMap: {
out: {
cx: 'calc(w)',
cy: 'calc(0.5 * h)',
label: 'Output',
labelPosition: 'outside',
labelColor: '#475569',
labelFontSize: 12,
labelFontFamily: 'monospace',
labelClassName: 'uppercase tracking-wide',
labelOffsetX: 10,
labelOffsetY: -2,
},
}

The label fields are:

  • label for the text itself
  • labelPosition for the label placement
  • labelColor, labelFontSize, and labelFontFamily for text styling
  • labelClassName for CSS classes
  • labelOffsetX and labelOffsetY override the layout-calculated x/y for fine placement

labelPosition selects a built-in JointJS port-label layout. Common choices are outside, inside, outsideOriented, insideOriented, left, right, top, and bottom. For the full layout model, including manual placement, see Port label layouts.

If a label should inherit a shared look, set those defaults on portStyle and only override the outliers in each port.

The preview below shows one node with four labeled ports at cardinal positions, each with different label styling:

Quick reference​

Declarative ports live in the element record's portMap, typed as Record<string, ElementPort>.

Each entry is an ElementPort.

Positioning​

  • cx: x position inside the element. It supports numbers and calc() expressions.
  • cy: y position inside the element. It supports numbers and calc() expressions.

Visuals​

  • width: port width. Default 8.
  • height: port height. Default 8.
  • color: fill color for the port body.
  • outline: outline color for the port body.
  • outlineWidth: outline width for the port body.
  • shape: 'ellipse', 'rect', or any SVG path string.
  • className: CSS class for the port body.

Behavior​

  • passive: makes the port target-only instead of link-starting. Default false.

Labels​

  • label: optional label text.
  • labelPosition: built-in JointJS label placement, such as outside, inside, left, or right.
  • labelColor: label text color.
  • labelFontSize: label font size.
  • labelFontFamily: label font family.
  • labelClassName: CSS class for the label.
  • labelOffsetX: overrides the layout-calculated x for the label.
  • labelOffsetY: overrides the layout-calculated y for the label.