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
RGLineTextand use the position/style fromgenerateLineTextStyle. - SVG path text can reuse
RGLinePath’suseTextOnPath, or callgenerateLineTextStyle4TextOnPathyourself. - For multiple labels, calculate positions from
linePathInfo.pointsorlinePathInfo.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.colorto express semantic line color. - Use
line.lineWidthto express line width. - Use
line.cssVarsto add complex theme variables. - Read variables such as
--rg-line-colorand--rg-line-widthin 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.showEndArrowor 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.