Export Graph Base64 Image Data
This example shows how to capture a full relation-graph scene as Base64 image data instead of downloading it immediately. It loads a static graph, prepares the canvas for export, captures it with a shared modern-screenshot helper, converts the blob to a data URL, and previews both the image and the raw string in a floating utility window.
Export a Relation Graph as Base64 Image Data
What This Example Builds
This example builds a read-only graph export utility that keeps the capture result in memory instead of downloading it immediately. The page shows a full-height relation-graph canvas, blue circular nodes, the built-in toolbar at the top-right, and a floating control window above the graph.
Users can choose an export background color, run a Generate Image Base64 action, preview the generated screenshot, and inspect the full data URL string in the same panel. The most important point is the workflow: the graph is prepared for image generation, captured as a blob, converted to Base64, and rendered back into the UI for immediate verification.
How the Data Is Organized
The graph data is declared inline as a single RGJsonData object inside initializeGraph(). It uses rootId: '2', a flat nodes array, and a flat lines array where each line already contains explicit from, to, and text fields.
There is no structural preprocessing before setJsonData(). The example assembles the static dataset in code, loads it into the graph instance, then recenters and fits the viewport. In a real application, the same shape could represent subsystem relationships, manufacturing structures, equipment assemblies, dependency graphs, or any network that later needs to be embedded as image data in another workflow.
How relation-graph Is Used
RGProvider wraps the example so relation-graph hooks can resolve the active graph instance. Inside MyGraph, RGHooks.useGraphInstance() is the main integration point: it loads the dataset with setJsonData(), aligns the viewport with moveToCenter() and zoomToFit(), and drives the export lifecycle through prepareForImageGeneration() and restoreAfterImageGeneration().
The example does not declare a named layout in graphOptions, so the rendered arrangement depends on relation-graph’s default behavior after the JSON data is loaded. The local options focus on presentation: toolBarDirection, toolBarPositionH, and toolBarPositionV keep the built-in toolbar visible as a horizontal control group at the top-right, while defaultLineColor, defaultNodeBorderColor, defaultNodeBorderWidth, defaultNodeWidth, defaultNodeHeight, defaultNodeShape, and defaultJunctionPoint establish the blue circular-node style.
There are no node, line, canvas, or viewport slots in this example, and it does not enable graph editing. Instead, it combines relation-graph with two local helper modules. domToImageByModernScreenshot.ts wraps modern-screenshot and exposes blobToBase64(...), while DraggableWindow.tsx provides the floating shell and the shared CanvasSettingsPanel. That shared panel uses RGHooks.useGraphStore() to read live option state and graphInstance.setOptions(...) to change wheel and drag behavior at runtime. The local SCSS file only provides selector scaffolding, so most visible styling comes from graph options and utility classes rather than deep stylesheet overrides.
Key Interactions
The graph initializes automatically on mount. Users do not need to arrange the view manually before export because the code loads the dataset, centers it, and fits it into the viewport immediately.
The main interaction is the Base64 capture flow. The color input updates the background color used for the next screenshot, and clicking Generate Image Base64 captures the prepared graph, restores graph state, converts the blob to a data URL, then shows both the preview image and the raw Base64 string.
The floating utility window can also be dragged, minimized, and switched into a settings overlay. That shared overlay changes wheelEventAction and dragEventAction on the live graph instance and exposes a separate download-based image export path, but those controls are reusable scaffolding around the example’s own Base64 workflow rather than its main lesson.
Key Code Fragments
This fragment shows that the example uses a static inline graph dataset and loads it directly into the instance before centering and fitting the viewport.
const myJsonData: RGJsonData = {
rootId: '2',
nodes: [
{ id: '2', text: 'ALTXX' }, { id: '3', text: 'CH2 TTN' }, { id: '4', text: 'CH1 AlCu' },
// ...
],
lines: [
{ id: 'l1', from: '2', to: '5', text: 'Subsystem' },
// ...
]
};
This fragment proves that relation-graph instance APIs handle the actual initialization work after the JSON structure is assembled.
await graphInstance.setJsonData(myJsonData);
graphInstance.moveToCenter();
graphInstance.zoomToFit();
This fragment is the core of the example: relation-graph prepares the graph for export, the shared helper captures the DOM, and the result is converted into Base64 state instead of being downloaded immediately.
const generateImageBase64 = async () => {
const canvasDom = await graphInstance.prepareForImageGeneration();
const imageBlob = await domToImageByModernScreenshot(canvasDom, {
backgroundColor: myImageBackgroundColor
});
await graphInstance.restoreAfterImageGeneration();
if (imageBlob) {
const base64 = await blobToBase64(imageBlob);
setBase64String(base64);
}
};
This fragment shows that the graph’s visible style is mostly defined through relation-graph options rather than custom render slots.
const graphOptions: RGOptions = {
toolBarDirection: 'h',
toolBarPositionH: 'right',
toolBarPositionV: 'top',
defaultLineColor: 'rgb(37 99 235)',
defaultNodeBorderColor: 'rgb(37 99 235)',
defaultNodeBorderWidth: 2,
defaultNodeWidth: 60,
defaultNodeHeight: 60,
defaultNodeShape: RGNodeShape.circle,
defaultJunctionPoint: RGJunctionPoint.border
};
This fragment shows the local UI controls that make the capture configurable and keep the result visible on the page.
<input
type="color"
value={myImageBackgroundColor}
onChange={(e) => setMyImageBackgroundColor(e.target.value)}
className="w-8 h-8 cursor-pointer border rounded"
/>
<SimpleUIButton onClick={generateImageBase64}>Generate Image Base64</SimpleUIButton>
{base64String && (
<div className="overflow-auto break-words h-36">{base64String}</div>
)}
This fragment shows the reusable helper layer behind the export flow: modern-screenshot produces the blob and FileReader turns that blob into a data URL.
const defaultOptions = {
scale: 2,
cacheBust: true,
...options
};
const blob = await domToBlob(element, defaultOptions);
export const blobToBase64 = (blob: Blob): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
resolve(reader.result);
};
reader.onerror = (error) => {
reject(error);
};
reader.readAsDataURL(blob);
});
};
What Makes This Example Distinct
The comparison data puts this example closest to generate-image and generate-image-with-watermark, but its emphasis is different. Those neighbors use the same prepare-capture-restore lifecycle, while this example pushes the result into component state as Base64 data and keeps both the preview and the raw string visible in the floating window.
It is also more configurable inside that in-memory workflow. The local background-color picker changes the exported image backdrop without changing graph options or stylesheet assets, which makes repeated capture and verification easier when the target context is unknown in advance.
Compared with toolbar-position and node-style2, the toolbar placement and blue default styling are supporting decisions rather than the main teaching point. The strongest combination here is a fixed top-right toolbar, a draggable shared utility shell, configurable export background, blob-to-Base64 conversion, and immediate on-page inspection of the generated data URL.
Where Else This Pattern Applies
This pattern applies to systems that need graph imagery as a value instead of a downloaded file: embedding snapshots into CMS content, sending graph previews through APIs, attaching data URLs to form submissions, or storing temporary screenshots in client-side state before another step completes.
It also fits debugging and integration scenarios. Keeping the preview image and the raw Base64 string on the page is useful when teams need to verify capture output, compare background settings, test downstream rendering, or troubleshoot how a relation-graph export behaves before wiring it into upload, clipboard, or message-delivery workflows.