JavaScript is required

Custom View and Background (#view / #background)

#view, #background, and #canvas-above are all extension layers outside or above the main graph body, but their coordinate systems and use cases are different.

  • #view: fixed relative to the RelationGraph component viewport. It does not follow canvas pan/zoom. Suitable for toolbars, menus, and panels.
  • #background: fixed relative to the viewport and located at the bottom of the canvas area. Suitable for background images, watermarks, and themed textures.
  • #canvas-above: uses canvas coordinates, follows canvas pan/zoom, and appears above nodes/lines.

1. #view Viewport Layer

#view is rendered into:

<div class="rg-graph-plugs">
  <div class="rg-view-slot">
    <!-- #view -->
  </div>
</div>

It is the top layer of the graph viewport and does not change with canvasOffset or canvasZoom.

Feature Description
Coordinate system RelationGraph component viewport coordinates, with (0, 0) at the top-left corner.
Follows canvas zoom/pan No.
Default events Outer .rg-graph-plugs uses pointer-events: none.
Suitable content Toolbar, context menu, property panel, minimap, editing controller, floating hint.
Unsuitable content Group boxes, annotations, or regions that should move with nodes/canvas.

Platform Syntax

Platform Syntax
Vue 3 / Vue 2 <template #view>...</template>
React <RGSlotOnView>...</RGSlotOnView>
Svelte <div slot="view">...</div>

Event Notes

The outer layer of #view uses pointer-events: none by default. If you place clickable UI there, enable events on the actual UI element:

<div class="my-panel rg-events-all">...</div>

Or:

<div class="my-panel" style="pointer-events: auto;">...</div>

Do not enable events on a full-screen overlay unless you intentionally want to block graph operations. Otherwise, it will block canvas dragging, node dragging, and line clicks.

2. #view Example: Fixed Toolbar

Vue3 example:

<script setup lang="ts">
import { shallowRef } from 'vue';
import type { RelationGraphInstance } from '@relation-graph/vue';

const graphInstance = shallowRef<RelationGraphInstance | null>(null);

function onReady(instance: RelationGraphInstance) {
  graphInstance.value = instance;
}

function centerGraph() {
  graphInstance.value?.moveToCenter();
}

function zoomIn() {
  graphInstance.value?.zoom(20);
}

function zoomOut() {
  graphInstance.value?.zoom(-20);
}
</script>

<template>
  <RelationGraph
    :options="graphOptions"
    :initial-data="graphData"
    @onReady="onReady"
  >
    <template #view>
      <div class="floating-toolbar rg-events-all">
        <button @click="centerGraph">Center</button>
        <button @click="zoomIn">Zoom In</button>
        <button @click="zoomOut">Zoom Out</button>
      </div>
    </template>
  </RelationGraph>
</template>
.floating-toolbar {
  position: absolute;
  right: 12px;
  top: 12px;
  display: flex;
  gap: 6px;
  padding: 6px;
  border-radius: 6px;
  background: #ffffff;
  box-shadow: 0 2px 10px rgba(15, 23, 42, 0.16);
}

Notes:

  • position: absolute is positioned relative to the RelationGraph component viewport.
  • The toolbar does not zoom with the canvas.
  • rg-events-all makes the buttons clickable.

3. #view Example: Context Menu

A context menu belongs in the viewport layer because it should appear at the mouse position on the screen, not be fixed to a canvas coordinate.

<script setup lang="ts">
import { ref, shallowRef } from 'vue';
import type { RelationGraphInstance } from '@relation-graph/vue';

const graphInstance = shallowRef<RelationGraphInstance | null>(null);
const menu = ref({
  visible: false,
  x: 0,
  y: 0,
  canvasX: 0,
  canvasY: 0
});

function onReady(instance: RelationGraphInstance) {
  graphInstance.value = instance;
}

function onContextmenu(
  event,
  objectType,
  object,
  eventPositionOnCanvas,
  eventPositionOnView
) {
  menu.value = {
    visible: true,
    x: eventPositionOnView.x,
    y: eventPositionOnView.y,
    canvasX: eventPositionOnCanvas.x,
    canvasY: eventPositionOnCanvas.y
  };
}

function createNodeHere() {
  if (!graphInstance.value) return;
  graphInstance.value.addNodes([
    {
      id: `node-${Date.now()}`,
      text: 'New Node',
      x: menu.value.canvasX,
      y: menu.value.canvasY
    }
  ]);
  menu.value.visible = false;
}
</script>

<template>
  <RelationGraph
    :options="graphOptions"
    :initial-data="graphData"
    @onReady="onReady"
    @onContextmenu="onContextmenu"
  >
    <template #view>
      <div
        v-if="menu.visible"
        class="context-menu rg-events-all"
        :style="{ left: menu.x + 'px', top: menu.y + 'px' }"
      >
        <button @click="createNodeHere">Create node here</button>
      </div>
    </template>
  </RelationGraph>
</template>

The onContextmenu event parameters provide the target type, target object, canvas coordinates, and viewport coordinates. This example stores both viewport coordinates and canvas coordinates:

Coordinate Use
menu.x/menu.y Position the context menu.
menu.canvasX/menu.canvasY Use as node canvas coordinates when creating a node.

4. #canvas-above Canvas Foreground Layer

#canvas-above uses the same canvas coordinate system as #canvas, but it is rendered at a higher layer.

Current render position:

<div class="rg-map-canvas rg-canvas-above">
  <div class="rg-canvas-slot rg-canvas-slot-above">
    <!-- #canvas-above -->
  </div>
</div>
Feature Description
Coordinate system Canvas coordinates.
Follows canvas zoom/pan Yes.
Layer Above nodes/lines.
Default events Affected by .rg-map-canvas { pointer-events: none; }.
Suitable content Selection hints, drag previews, alignment guides, temporary anchors above nodes.

Example: show a temporary selection box in canvas coordinates:

<template #canvas-above>
  <div
    v-if="selectionBox.visible"
    class="selection-box"
    :style="{
      left: selectionBox.x + 'px',
      top: selectionBox.y + 'px',
      width: selectionBox.width + 'px',
      height: selectionBox.height + 'px'
    }"
  />
</template>
.selection-box {
  position: absolute;
  box-sizing: border-box;
  border: 1px solid #3b82f6;
  background: rgba(59, 130, 246, 0.12);
  pointer-events: none;
}

If foreground content needs to be clicked, also add rg-events-all or pointer-events: auto.

5. #background Background Layer

#background is rendered into:

<div class="rg-map-background">
  <!-- #background -->
</div>
Feature Description
Coordinate system RelationGraph component viewport coordinates.
Follows canvas zoom/pan No.
Layer Bottom of the canvas area.
Default events .rg-map-background uses pointer-events: none.
Suitable content Background images, watermarks, themed textures, static decoration.

Vue syntax:

<template #background>
  <div class="graph-background">
    <div class="watermark">Relation Graph</div>
  </div>
</template>

React syntax:

import { RelationGraph, RGBackground } from '@relation-graph/react';

<RelationGraph options={graphOptions} initialData={graphData}>
  <RGBackground>
    <div className="graph-background">
      <div className="watermark">Relation Graph</div>
    </div>
  </RGBackground>
</RelationGraph>

Styles:

.graph-background {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(rgba(148, 163, 184, 0.16) 1px, transparent 1px),
    linear-gradient(90deg, rgba(148, 163, 184, 0.16) 1px, transparent 1px);
  background-size: 24px 24px;
}

.graph-background .watermark {
  position: absolute;
  right: 18px;
  bottom: 14px;
  font-size: 12px;
  color: rgba(100, 116, 139, 0.5);
}

Notes:

  • The background layer does not zoom with the canvas, so it is suitable for a “screen background”, not for a grid in graph coordinates. If the grid should move and scale with nodes, put it in #canvas.
  • The current background is part of the graph DOM. Custom backgrounds usually participate in image export. If the background uses cross-origin images, browser canvas tainting rules still apply.

6. Which Layer Built-in Components Belong To

relation-graph exposes several built-in components. They are usually more suitable for #view.

Component Recommended layer Description
RGToolBar / RGMiniToolBar #view Fixed toolbar; it should not zoom with the canvas.
RGMiniView #view The minimap is usually fixed in a corner.
RGEditingNodeController #view or internal editing layer Node editing controller, usually maps node position to viewport coordinates.
RGEditingLineController #view or internal editing layer Line editing controller.
RGEditingConnectController #view or internal editing layer Line creation/connection controller.
RGWatermark #background or #view Put pure background watermarks in background; put fixed overlay watermarks in view.
RGBackground #background / React children Used by React to identify the background slot.

Specific usage can vary slightly by platform wrapper. The principle is: fixed UI goes in #view, background decoration goes in #background, and canvas-coordinate content goes in #canvas or #canvas-above.

7. Relationship with showToolBar

options.showToolBar controls whether the built-in small toolbar is shown. In the source, when showToolBar is true, the built-in GraphXsToolBar is rendered automatically.

Option Behavior
showToolBar: true Show the built-in toolbar.
showToolBar: false Hide the built-in toolbar; you can place a custom toolbar in #view.

If you want a fully custom toolbar, usually set:

const graphOptions = {
  showToolBar: false
};

Then implement your own zoom, centering, refresh, export, and other operations in #view.

8. Layer Selection Guide

Requirement Recommended layer Reason
Toolbar fixed in the top-right corner #view Does not follow canvas zoom.
Business floating card to the right of a node, moving with the node #canvas-above or node slot Needs canvas coordinates.
Context menu #view Menu position comes from mouse viewport coordinates.
Coordinate hint while creating a node #view + coordinate conversion UI is fixed; business coordinates are converted through APIs.
Group background or lane #canvas Belongs to graph coordinates and should be below nodes.
Selection rectangle #canvas-above Belongs to graph coordinates and should be above nodes.
Static watermark #background Does not need interaction and does not move with the canvas.
Grid that follows graph zoom #canvas Uses canvas coordinates.
Background grid that does not follow graph zoom #background Uses viewport coordinates.

9. FAQ

Why cannot I click a button inside #view?

Because the outer .rg-graph-plugs uses pointer-events: none by default. Add class="rg-events-all" or style="pointer-events:auto" to the actual panel or button.

Why is the context menu positioned incorrectly after zooming?

The context menu itself should use viewport coordinates: getViewXyByEvent(event). If a menu action needs to create a node on the canvas, additionally convert the viewport coordinates to canvas coordinates with getCanvasXyByViewXy(viewPoint).

Why does the background image not move with the graph?

#background is a viewport background and does not follow the canvas. If the background image should move and scale with nodes, put it in #canvas.

Why does #canvas-above content block nodes?

It is designed to be above nodes/lines. If it is only a visual hint, set pointer-events: none; if only a small local area needs interaction, enable events only on the specific button or anchor.

Can #view read the current zoom and offset?

Yes. Read graph state through hooks or instance APIs, such as RGHooks.useGraphInstance() and RGHooks.useViewInformation(). Vue3 currently also exports RGHooks.useGraphOptions(). React/Svelte can use useGraphStore() or more specific state hooks according to their platform. If you need to map canvas coordinates to the viewport, prefer getViewXyByCanvasXy.

10. Next Reading