JavaScript is required

Custom Lines (#line)

The line slot replaces the SVG/text rendering of a line. It does not receive the original JsonLine; it receives lineConfig, which has already been calculated by the graph instance. lineConfig contains the current line object, start target, end target, multi-line offset information, and default options, so it is suitable for directly generating paths, arrows, and labels.

If you only need to change line color, width, shape, dash style, arrows, or text position, prefer the fields described in Line Data Model. Use #line when you need custom path structures, multiple labels, status icons, or complex HTML for line text.

1. Platform Syntax

Platform Syntax Description
Vue 3 / Vue 2 <template #line="{ lineConfig, checked, graphInstanceId, defaultLineTextOnPath }"> Recommended syntax.
React <RGSlotOnLine>{props => ...}</RGSlotOnLine> children must be a function.
React lineSlot={({ lineConfig }) => ...} Prop-style syntax. Do not use it together with <RGSlotOnLine>.
Svelte <g slot="line" let:lineConfig let:checked let:graphInstanceId> Content is inside an SVG <g>; the root element should usually be an SVG element.

2. Slot Props

Type definition in the source:

export type RGLineSlotProps = {
  lineConfig: RGGenerateLineConfig;
  checked?: boolean;
  defaultLineTextOnPath?: boolean;
  graphInstanceId?: string;
};
Prop Type Role
lineConfig RGGenerateLineConfig Render configuration for the current line. Normal lines come from generateLineConfig(line), fake lines come from generateFakeLineConfig(fakeLine).
checked boolean | undefined Whether the current line is marked as checked by the internal controller.
defaultLineTextOnPath boolean | undefined Global default for path-following text, from options.defaultLineTextOnPath.
graphInstanceId string | undefined Current graph instance ID, used to generate non-conflicting SVG path ids.

RGGenerateLineConfig:

export type RGGenerateLineConfig = {
  line: RGLine | RGFakeLine;
  from?: RGLineTarget;
  to?: RGLineTarget;
  totalLinesBetweenNodes?: number;
  currentLineIndex?: number;
  defaultOptions?: any;
};
Field Type Description
line RGLine | RGFakeLine Current line object being rendered. Normal lines contain data fields such as from, to, and text; fake lines contain temporary targets used during dragging.
from RGLineTarget | undefined Start target. For normal lines, this is usually a node boundary or a specified connection point.
to RGLineTarget | undefined End target.
totalLinesBetweenNodes number | undefined Total number of lines between the two nodes, used when calculating multi-line spacing.
currentLineIndex number | undefined Index of the current line in the multi-line group.
defaultOptions any Defaults needed for current line calculation, such as default line shape and junction point rule.

lineConfig may fail to be generated when nodes are hidden, endpoints cannot be resolved, EasyView is active, or other internal conditions apply. In the source, RGLinePeel renders the slot only when config exists, so when you enter #line, it is usually already in a renderable state.

3. Common Fields on line

For the complete field list, see Line Data Model. In line slots, these fields are most commonly used:

Field Type Use
line.id string Unique line ID. Custom SVG path ids usually combine graphInstanceId and line.id.
line.text string | undefined Default line text.
line.type string | undefined Line type. It can dispatch business-specific templates.
line.data Record<string, any> | undefined Business data, such as status, weight, start/end labels.
line.color string | undefined Line color. It is written to --rg-line-color.
line.lineWidth number | undefined Line width in px. It is written to --rg-line-width.
line.opacity number | undefined Opacity.
line.lineShape RGLineShape | undefined Line shape. It affects the result of generateLinePath.
line.dashType number | string | undefined Suffix for the dash style class.
line.animation number | string | undefined Suffix for the line animation class.
line.useTextOnPath boolean | undefined Whether text should follow the path.
line.showStartArrow / line.showEndArrow boolean | undefined Whether to show start/end arrows.
line.startMarkerId / line.endMarkerId string | undefined Custom marker id.
line.cssVars Record<string, string> Additional CSS variables applied to the line wrapper.
line.isFakeLine boolean | undefined Whether this is a fake line. It may be true while dragging or editing a line.

4. Reusable Built-in Components and Instance APIs

The current Vue3/React/Svelte packages all expose RGLinePath and RGLineText; Vue3/React also use them in the default line implementation.

Name Type Role
RGLinePath Component Renders SVG path, arrow markers, and path-following text from lineConfig and linePathInfo.
RGLineText Component Renders HTML line text into the internal line text container.
graphInstance.generateLinePath(lineConfig) Instance API Generates RGLinePathInfo, including path data, text position, key points, and related information.
graphInstance.generateLineTextStyle(lineConfig, linePathInfo) Instance API Generates text content and CSS style for normal HTML line text.
graphInstance.generateLineTextStyle4TextOnPath(lineConfig) Instance API Generates offset, anchor, text, and other information needed by SVG <textPath>.
graphInstance.getArrowMarkerId(line, isStartArrow) Instance API Gets the start/end arrow marker reference.
graphInstance.onLineClick(line, event) Instance API Triggers built-in line click logic and external events.

RGLinePathInfo:

export type RGLinePathInfo = {
  pathData: string;
  pathCommands: any[];
  textPosition: RGCoordinate;
  points: RGCoordinate[];
  startDirection?: string;
  endDirection?: string;
};
Field Type Description
pathData string SVG path string that can be used directly as <path d="...">.
pathCommands any[] Command collection used to generate the path, useful for advanced customization.
textPosition { x, y } Default text position.
points RGCoordinate[] Key points calculated for the current line.
startDirection / endDirection string | undefined Start/end direction, mainly used by advanced arrow and polyline logic.

5. Reuse the Default Path and Customize Line Content

Vue3 example:

<script setup lang="ts">
import { computed } from 'vue';
import {
  RGLinePath,
  RGLineText,
  RGHooks,
  type RGLineSlotProps
} from '@relation-graph/vue';

const props = defineProps<RGLineSlotProps>();
const graphInstance = RGHooks.useGraphInstance();

const linePathInfo = computed(() => graphInstance.generateLinePath(props.lineConfig));
const textStyle = computed(() => {
  return graphInstance.generateLineTextStyle(props.lineConfig, linePathInfo.value);
});

function onLineClick(event: MouseEvent | TouchEvent) {
  graphInstance.onLineClick(props.lineConfig.line, event);
}
</script>

<template>
  <RGLinePath
    :line-config="lineConfig"
    :line-path-info="linePathInfo"
    :checked="checked"
    :graph-instance-id="graphInstanceId"
    :use-text-on-path="false"
    @onLineClick="onLineClick"
  />

  <RGLineText
    v-if="lineConfig.line.text"
    :line-config="lineConfig"
    :line-path-info="linePathInfo"
    :checked="checked"
  >
    <div
      class="my-line-label"
      :style="textStyle.cssStyles"
      @click="onLineClick"
    >
      {{ textStyle.text }}
      <span v-if="lineConfig.line.data?.status" class="status">
        {{ lineConfig.line.data.status }}
      </span>
    </div>
  </RGLineText>
</template>

Use it in a page:

<RelationGraph :options="graphOptions" :initial-data="graphData">
  <template #line="lineSlotProps">
    <MyLineContent v-bind="lineSlotProps" />
  </template>
</RelationGraph>

6. React Example

import {
  RelationGraph,
  RGSlotOnLine,
  RGLinePath,
  RGLineText,
  RGHooks,
  RGLineShape,
  type RGLineSlotProps
} from '@relation-graph/react';

function MyLineContent({
  lineConfig,
  checked,
  graphInstanceId,
  defaultLineTextOnPath
}: RGLineSlotProps) {
  const graphInstance = RGHooks.useGraphInstance();
  const linePathInfo = graphInstance.generateLinePath(lineConfig);
  const textStyle = graphInstance.generateLineTextStyle(lineConfig, linePathInfo);
  const useTextOnPath = !!(lineConfig.line.useTextOnPath || defaultLineTextOnPath);
  const useSvgTextPath = useTextOnPath && lineConfig.line.lineShape !== RGLineShape.StandardStraight;

  const onLineClick = (event: React.MouseEvent | React.TouchEvent) => {
    graphInstance.onLineClick(lineConfig.line, event.nativeEvent);
  };

  return (
    <>
      <RGLinePath
        lineConfig={lineConfig}
        linePathInfo={linePathInfo}
        checked={checked}
        graphInstanceId={graphInstanceId}
        useTextOnPath={useSvgTextPath}
        onLineClick={onLineClick}
      />

      {lineConfig.line.text && !useSvgTextPath && (
        <RGLineText
          lineConfig={lineConfig}
          linePathInfo={linePathInfo}
          checked={checked}
        >
          <div
            className="my-line-label"
            style={textStyle.cssStyles}
            onClick={onLineClick}
          >
            {textStyle.text}
            {lineConfig.line.data?.status && (
              <span className="status">{lineConfig.line.data.status}</span>
            )}
          </div>
        </RGLineText>
      )}
    </>
  );
}

<RelationGraph options={graphOptions} initialData={graphData}>
  <RGSlotOnLine>
    {(props) => <MyLineContent {...props} />}
  </RGSlotOnLine>
</RelationGraph>

7. Fully Custom SVG Path

If you do not reuse RGLinePath, you can render SVG yourself. In that case, keep these capabilities:

Capability Why it matters
Use generateLinePath(lineConfig) Keeps built-in line shapes, multi-line offsets, and junction point rules consistent.
Keep a thicker transparent click path Thin lines are hard to click. The default implementation uses .rg-line-bg for clicks.
Add rg-line-peel and data-id to the outer <g> Helps internal isLine(el), CSS state, and debugging.
Use getArrowMarkerId Keeps default arrow markers, custom markers, and options consistent.
Handle disablePointEvent / opacity === 0 Disabled lines should not continue responding to clicks.

Example:

<template #line="{ lineConfig, checked, graphInstanceId }">
  <CustomSvgLine
    :line-config="lineConfig"
    :checked="checked"
    :graph-instance-id="graphInstanceId"
  />
</template>

CustomSvgLine.vue:

<script setup lang="ts">
import { computed } from 'vue';
import { RGHooks, type RGLineSlotProps } from '@relation-graph/vue';

const props = defineProps<RGLineSlotProps>();
const graphInstance = RGHooks.useGraphInstance();

const pathInfo = computed(() => graphInstance.generateLinePath(props.lineConfig));
const line = computed(() => props.lineConfig.line);
const startMarker = computed(() => graphInstance.getArrowMarkerId(line.value, true));
const endMarker = computed(() => graphInstance.getArrowMarkerId(line.value, false));
const pathId = computed(() => `${props.graphInstanceId}-${line.value.id}`);

function onLineClick(event: MouseEvent | TouchEvent) {
  graphInstance.onLineClick(line.value, event);
}
</script>

<template>
  <g
    class="rg-line-peel my-svg-line"
    :class="[
      line.className,
      line.selected && 'rg-line-selected',
      checked && 'rg-line-checked',
      (line.disablePointEvent || line.opacity === 0) && 'rg-line-disable-events'
    ]"
    :data-id="line.id"
    :style="{
      '--rg-line-color': line.color,
      '--rg-line-width': line.lineWidth ? line.lineWidth + 'px' : undefined,
      '--rg-line-opacity': line.opacity,
      '--rg-line-marker-start': startMarker,
      '--rg-line-marker-end': endMarker,
      ...(line.cssVars || {})
    }"
  >
    <path
      :d="pathInfo.pathData"
      class="rg-line-bg"
      fill="none"
      stroke="transparent"
      stroke-width="12"
      @click="onLineClick"
    />
    <path
      :id="pathId"
      :d="pathInfo.pathData"
      class="rg-line"
      fill="none"
      :marker-start="startMarker"
      :marker-end="endMarker"
    />
  </g>
</template>

8. Path-following Text and Normal Text

Current default relation-graph logic:

Situation Default behavior
line.useTextOnPath is true Tries to use path-following text.
options.defaultLineTextOnPath is true Lines without individual configuration also try to use path-following text.
Line shape is RGLineShape.StandardStraight The default implementation does not use SVG <textPath>; it uses normal HTML text.
Other line shapes with path text enabled The default implementation uses SVG <textPath>.
Path text is not enabled The default implementation uses RGLineText to render an HTML label.

When customizing text:

  • Normal HTML labels can reuse RGLineText and use the position/style from generateLineTextStyle.
  • SVG path text can reuse RGLinePath’s useTextOnPath, or call generateLineTextStyle4TextOnPath yourself.
  • For multiple labels, calculate positions from linePathInfo.points or linePathInfo.textPosition.

9. Handling Fake Lines

RGFakeLine may appear while creating or editing a line. In the source, RGLinePeel chooses:

line.isFakeLine
  ? graphInstance.generateFakeLineConfig(line)
  : graphInstance.generateLineConfig(line)

Therefore your #line may receive either a normal line or a fake line. Check it with:

const isFakeLine = !!lineConfig.line.isFakeLine;

Common fake line characteristics:

Characteristic Description
line.isFakeLine is true It is not a persistent line from data.
Start/end targets may be nodes, node points, canvas points, HTML elements, and so on It depends on the target type during editing/dragging.
It usually should not be saved into business data It is used for temporary visual feedback.
Slot content should be compatible with it Otherwise the temporary line may disappear during line dragging.

If your business custom line does not need special handling for fake lines, you can still use generateLinePath(lineConfig) and let the instance calculate endpoints.

10. Line Styling Recommendations

Use CSS variables to receive data fields:

.my-svg-line .rg-line {
  stroke: var(--rg-line-color);
  stroke-width: var(--rg-line-width);
  opacity: var(--rg-line-opacity);
  marker-start: var(--rg-line-marker-start);
  marker-end: var(--rg-line-marker-end);
}

.my-svg-line.rg-line-selected .rg-line {
  filter: drop-shadow(0 0 3px #3b82f6);
}

.my-line-label {
  padding: 2px 6px;
  border-radius: 4px;
  background: #ffffff;
  color: var(--rg-line-fontcolor);
  font-size: var(--rg-line-fontsize);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08);
  pointer-events: auto;
}

Do not only hard-code stroke and stroke-width inside custom paths. Prefer:

  • Use line.color to express semantic line color.
  • Use line.lineWidth to express line width.
  • Use line.cssVars to add complex theme variables.
  • Read variables such as --rg-line-color and --rg-line-width in CSS.

11. FAQ

Why cannot I click my custom line?

Thin SVG lines are hard to click. The default implementation renders a .rg-line-bg path with a wider transparent stroke for click handling. Custom lines should also keep:

<path class="rg-line-bg" d="..." stroke="transparent" stroke-width="12" fill="none" />

Why is the arrow not shown?

Check:

  • Whether you used graphInstance.getArrowMarkerId(line, true/false).
  • Whether the result is assigned to marker-start / marker-end.
  • Whether a unique path id is generated to avoid multi-instance conflicts.
  • Whether line.showStartArrow / line.showEndArrow or the default marker configuration allows the arrow to show.

Why is the text position wrong?

For normal text, use generateLineTextStyle(lineConfig, linePathInfo). It considers line shape, multi-line offset, and text position configuration. For fully custom text, you need to handle fields such as line.textOffsetX, line.textOffsetY, line.placeText, and line.textAnchor yourself.

Why are lineConfig.from or lineConfig.to missing?

The config may fail to be generated when nodes are hidden, endpoint targets cannot be resolved, or a temporary line target does not exist. The current component renders #line only when config exists, so regular slot code rarely sees this. If you call generateLineConfig yourself, handle the false return value.

Can I render HTML inside #line?

Yes, but the line slot itself is inside an SVG <g>. Writing HTML directly there may be invalid or inconsistent across browsers. Prefer the built-in RGLineText, which renders HTML labels into the internal text container; or use SVG <foreignObject>, but then you must handle browser compatibility and sizing yourself.

12. Next Reading