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 (
cxandcy) - what does it look like (
width,height,color,outline,shape) - how should it behave (
passiveand 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.
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.
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, or40place the port at a fixed offset - expressions like
calc(w),calc(0.5 * h), orcalc(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:
widthandheightfor the rendered sizecolorfor the filloutlineandoutlineWidthfor the strokeshapefor the port bodyclassNamefor 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
dattribute
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:
portStyleprovides defaults for every port inportMap- 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:
labelfor the text itselflabelPositionfor the label placementlabelColor,labelFontSize, andlabelFontFamilyfor text stylinglabelClassNamefor CSS classeslabelOffsetXandlabelOffsetYoverride 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 andcalc()expressions.cy: y position inside the element. It supports numbers andcalc()expressions.
Visuals​
width: port width. Default8.height: port height. Default8.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. Defaultfalse.
Labels​
label: optional label text.labelPosition: built-in JointJS label placement, such asoutside,inside,left, orright.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.