Connector must render inside a Node. It owns (or adopts) a core ConnectorMirror registered against the parent node.

PropMeaning
idStable domain identity; minted when omitted — supply it for persistence
nameRequired. The connector’s key within its parent node
rulesPartial<ConnectorRules> admission rules (see below)
metadataSnapLineMetadata attached to the connector
callbacksConnectorCallbacks (new-line payload resolver plus pointer, candidate, and connection events)
surfaceStrategiesCustom target hit-test and anchor strategies
childrenSnippet receiving an action; apply it with use: to one custom HTML or SVG input root
colliderRadiusTarget snapping radius override
edgePanPan the camera when dragging a line near the viewport edge (default true)
connectorObjectAdopt an existing ConnectorMirror
dataExtra data-* attributes for the port element

object() returns the underlying ConnectorMirror. name and connectorObject are construction-time identities; every other prop is reactive.

Without a child snippet, Connector renders its default port div. A custom root can be an SVG stroke:

<svg viewBox="0 0 160 80">
  <Connector name="surface">
    {#snippet children(bindConnector)}
      <path
        use:bindConnector
        d="M 0 0 H 160 V 80 H 0 Z"
        fill="none"
        stroke="transparent"
        stroke-width="16"
        pointer-events="stroke"
      />
    {/snippet}
  </Connector>
</svg>

The custom element is the native source hit area. SnapLine does not run a manual source hit test. For target snapping, set colliderRadius large enough to enclose the custom shape; any configured targetHitTest callbacks replace the default center candidate inside that broad-phase circle.

Connection rules

RuleDefaultMeaning
maxOutgoing"unlimited"Maximum outgoing lines (previews reserve a slot); 0 makes the connector target-only
maxIncoming1Maximum settled incoming lines; 0 makes the connector source-only
reconnecttrueAllow dragging a settled line’s end to a new target
allowParallelfalseBoth endpoints must allow parallel lines between the same pair
onFull"reject"Policy when a proposal would exceed maxIncoming: "reject" it, or "replace-oldest" to evict the oldest incoming lines
isValidConnection—Line-aware admission predicate; synchronous and side-effect free, called repeatedly during candidate discovery and rechecked on drop. Either endpoint may veto

An omitted rules prop therefore yields an unlimited-outgoing, single-incoming connector that rejects extra incoming lines.

Warning

Topology methods on ConnectorMirror such as createLine, deleteLine, and deleteAllLines are @internal. Change topology by updating your canonical LineRecords through ControlledGraph.

Spawning a node from an empty-space drop

onDragEnd reports why a drag ended through outcome:

outcomeMeaning
"connected"Settled onto an admitting target
"refused"Dropped over a target that declined (capacity, predicate, roles)
"empty-space"No connector was resolved under the pointer
"cancelled"Pointer cancel or teardown

"empty-space" is the hook for “drag a line out and drop it on the canvas to make a new node”. SnapLine deliberately has no node-change request channel: your framework decides when a node exists, so you mount the node and add the line record yourself.

onDragEnd: ({ outcome, position, connector }) => {
  if (outcome !== "empty-space") return;
  const nodeId = mint();
  nodes = [...nodes, { id: nodeId, x: position.x, y: position.y }];
  graph.setLines([
    ...lines,
    {
      id: mint(),
      fromConnectorId: connector.connectorId,
      toConnectorId: `${nodeId}:input`,
    },
  ]);
};

The record names a connector that has not mounted yet. That is fine: it stays latent, and connector registration schedules the pass that converges it, so the line appears the moment the node does. Note the dragged preview is discarded at drop like any unaccepted gesture — the new line appears with the node rather than continuing the stroke.

A role-ineligible connector (maxIncoming: 0) is filtered out during candidate resolution, so dropping on one reports "empty-space", not "refused". A refusal is a connector that resolves as a candidate and then declines at drop, which is what isValidConnection’s phase distinguishes.