The library separates core functionality from React components:
// Core functionality (no React dependency)
import { Graph } from "@gravity-ui/graph";
// React components (requires React)
import { GraphCanvas, GraphBlock, GraphBlockAnchor, useGraph, useGraphEvent } from "@gravity-ui/graph/react";The library uses a modular architecture that separates the core rendering engine from React-specific implementations:
- Core Layer: Handles canvas rendering, event management, and data structures
- GraphCanvas: A React component that creates and manages the ReactLayer
This separation allows the core library to be framework-agnostic while providing seamless React integration.
The main container component that renders your graph:
import { GraphCanvas } from "@gravity-ui/graph/react";
<GraphCanvas
graph={graph}
renderBlock={renderBlock}
className="my-graph"
blockListClassName="custom-blocks-container"
reactLayerRef={reactLayerRef}
/>graph(required): The Graph instance to renderrenderBlock(optional): Function to render custom block componentsclassName(optional): CSS class for the main containerblockListClassName(optional): CSS class applied to the blocks container layerreactLayerRef(optional): Ref to access the ReactLayer instance directly- Event callbacks: All graph event callbacks can be passed as props
The GraphBlock component is a crucial wrapper that handles the complex interaction between HTML elements and the canvas layer. It's responsible for:
- Position Synchronization: Automatically aligns HTML content with canvas coordinates
- State Management: Handles selection, hover states, and drag interactions
- Layout Management: Maintains proper block dimensions and positioning
- Z-index Handling: Manages proper layering of blocks
- CSS Variables: Injects position and state variables for styling
import { GraphBlock } from "@gravity-ui/graph/react";
<GraphBlock
graph={graph}
block={block}
className="custom-block"
containerClassName="custom-container"
>
<div>Your block content here</div>
</GraphBlock>The component automatically inherits styles from graph settings but can be customized using CSS variables:
.custom-block {
/* Position variables (automatically set by GraphBlock) */
--graph-block-geometry-x: 0px;
--graph-block-geometry-y: 0px;
--graph-block-geometry-width: 200px;
--graph-block-geometry-height: 100px;
--graph-block-z-index: 1;
--graph-block-order: 0;
/* Theme variables (from graph settings) */
--graph-block-bg: rgba(37, 27, 37, 1);
--graph-block-border: rgba(229, 229, 229, 0.2);
--graph-block-border-selected: rgba(255, 190, 92, 1);
/* Custom styling */
background-color: var(--graph-block-bg);
border: 1px solid var(--graph-block-border);
}
.custom-block.selected {
border-color: var(--graph-block-border-selected);
}Renders connection points on blocks. The component supports two positioning modes:
- fixed - Anchors are placed at exact coordinates relative to the block:
import { GraphBlockAnchor } from "@gravity-ui/graph/react";
<GraphBlockAnchor
graph={graph}
anchor={anchor}
position="fixed"
>
{(state) => (
<div className={state.selected ? 'selected' : ''}>
{/* Anchor visuals */}
</div>
)}
</GraphBlockAnchor>- auto - Anchors are automatically positioned based on their type:
<GraphBlockAnchor
graph={graph}
anchor={anchor}
position="auto"
// Inputs will be placed on the left, outputs on the right
>
{(state) => (
<div className={state.selected ? 'selected' : ''}>
{/* Anchor visuals */}
</div>
)}
</GraphBlockAnchor>Anchor styling also uses CSS variables:
.anchor {
--graph-block-anchor-bg: rgba(255, 190, 92, 1);
--graph-block-anchor-border-selected: rgba(255, 190, 92, 1);
}The library provides a comprehensive set of React hooks for working with the graph:
| Hook | Description |
|---|---|
useGraph |
Create and manage a Graph instance |
useGraphEvent |
Subscribe to a single graph event |
useGraphEvents |
Subscribe to multiple graph events |
useLayer |
Add and manage layers |
useBlockState |
Subscribe to block state changes |
useBlockViewState |
Get block view component |
useBlockAnchorState |
Subscribe to anchor state changes |
useSignal |
Subscribe to signal values |
useComputedSignal |
Create computed signals |
useSignalEffect |
Run effects on signal changes |
useSchedulerDebounce |
Create debounced function with frame timing |
useSchedulerThrottle |
Create throttled function with frame timing |
useScheduledTask |
Schedule task for frame-based execution |
useSceneChange |
React to scene updates (camera, viewport) |
For detailed documentation of all hooks, see React Hooks Reference.
import { useGraph, useGraphEvent, useBlockState } from '@gravity-ui/graph/react';
function MyGraph() {
const { graph, setEntities, start } = useGraph({
settings: { canDragBlocks: true },
});
// Subscribe to events
useGraphEvent(graph, "block-change", ({ block }) => {
console.log("Block changed:", block);
});
// Track block state
const blockState = useBlockState(graph, "block-1");
return <GraphCanvas graph={graph} />;
}The graph can be extensively configured through the useGraph hook. See the full configuration reference for details.
In addition to the imperative useLayer hook, the library provides declarative React components for working with layers and HTML overlays.
The GraphLayer component provides a declarative way to add existing Layer classes to the graph:
import { GraphLayer, GraphCanvas, useGraph } from '@gravity-ui/graph/react';
import { DevToolsLayer } from '@gravity-ui/graph/plugins';
function MyGraph() {
const { graph, setEntities, start } = useGraph({});
React.useEffect(() => {
if (graph) {
setEntities({ blocks: [...], connections: [...] });
start();
}
}, [graph]);
return (
<GraphCanvas graph={graph} renderBlock={renderBlock}>
<GraphLayer
layer={DevToolsLayer}
showRuler={true}
showCrosshair={true}
rulerSize={20}
rulerBackgroundColor="rgba(0, 0, 0, 0.8)"
crosshairColor="rgba(255, 0, 0, 0.8)"
/>
</GraphCanvas>
);
}The GraphPortal component allows creating HTML layers without writing separate Layer classes:
import { GraphPortal, GraphCanvas, useGraph } from '@gravity-ui/graph/react';
function MyGraph() {
const { graph, setEntities, start } = useGraph({});
const [counter, setCounter] = useState(0);
React.useEffect(() => {
if (graph) {
setEntities({ blocks: [...], connections: [...] });
start();
}
}, [graph]);
return (
<GraphCanvas graph={graph} renderBlock={renderBlock}>
<GraphPortal zIndex={200}>
<div style={{
position: 'absolute',
top: 20,
right: 20,
background: 'white',
padding: 16,
borderRadius: 8,
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)'
}}>
<h3>Custom UI</h3>
<button onClick={() => setCounter(c => c + 1)}>
Counter: {counter}
</button>
</div>
</GraphPortal>
</GraphCanvas>
);
}For more detailed information about declarative components, see the dedicated documentation.
const config = {
viewConfiguration: {
colors: {
block: {
background: "rgba(37, 27, 37, 1)",
border: "rgba(229, 229, 229, 0.2)",
selectedBorder: "rgba(255, 190, 92, 1)"
},
connection: {
background: "rgba(255, 255, 255, 0.5)",
selectedBackground: "rgba(234, 201, 74, 1)"
},
anchor: {
background: "rgba(255, 190, 92, 1)"
},
canvas: {
layerBackground: "rgba(22, 13, 27, 1)",
dots: "rgba(255, 255, 255, 0.2)"
}
},
constants: {
block: {
SCALES: [0.1, 0.2, 0.5], // Zoom levels for block rendering
}
}
}
};const config = {
settings: {
// Camera controls
canDragCamera: true,
canZoomCamera: true,
// Block interactions
canDragBlocks: true,
canDuplicateBlocks: false,
canChangeBlockGeometry: 'ALL', // 'NONE' | 'ALL' | 'SELECTED'
// Connection settings
canCreateNewConnections: true,
showConnectionArrows: true,
useBezierConnections: true,
// Visual settings
scaleFontSize: 1,
useBlocksAnchors: true,
showConnectionLabels: false,
// Custom block components
blockComponents: {
'action-block': ActionBlockComponent,
'text-block': TextBlockComponent
}
}
};The library provides a rich set of events you can listen to:
import { useGraphEvent } from '@gravity-ui/graph/react';
// When a new connection is created
useGraphEvent(graph, "connection-created",
({ sourceBlockId, targetBlockId }, event) => {
// Handle new connection
// Use event.preventDefault() to cancel if needed
});
// When blocks are selected
useGraphEvent(graph, "blocks-selection-change",
({ changes }) => {
console.log('Added:', changes.add);
console.log('Removed:', changes.removed);
});
// When a block is modified
useGraphEvent(graph, "block-change",
({ block }) => {
// Handle block changes
});// Zoom to center with padding
graph.zoomTo("center", { padding: 300 });
// Zoom to specific blocks
graph.zoomTo([blockId1, blockId2], {
transition: 250
});useGraphEvent(graph, "blocks-selection-change",
({ changes }) => {
const selectedBlocks = changes.add.map(id =>
graph.rootStore.blocksList.getBlock(id)
);
// Update your UI with selected blocks
});useGraphEvent(graph, "connection-created",
(connection, event) => {
event.preventDefault(); // Prevent default connection
// Apply your own connection logic
if (validateConnection(connection)) {
graph.api.addConnection({
...connection,
// Add custom properties
});
}
});Here's a practical example that demonstrates the core features:
import React, { useCallback } from 'react';
import { Graph } from '@gravity-ui/graph';
import { GraphCanvas, GraphBlock, GraphBlockAnchor, useGraph, useGraphEvent, TBlock } from '@gravity-ui/graph/react';
function BlockComponent({ block, graph }: { block: TBlock; graph: Graph }) {
return (
<GraphBlock
graph={graph}
block={block}
className="custom-block"
>
{/* Block content */}
<div className="block-content">
{block.name}
</div>
{/* Render anchors */}
{block.anchors?.map(anchor => (
<GraphBlockAnchor
key={anchor.id}
graph={graph}
anchor={anchor}
position="fixed"
>
{(state) => (
<div className={`anchor ${state.selected ? 'selected' : ''}`}>
<div className="anchor-dot" />
{state.isConnecting && (
<div className="anchor-label">
{anchor.type === 'IN' ? 'Input' : 'Output'}
</div>
)}
</div>
)}
</GraphBlockAnchor>
))}
</GraphBlock>
);
}
function CustomGraph() {
// Initialize graph
const { graph, setEntities, start } = useGraph({
viewConfiguration: {
colors: {
block: {
background: "rgba(37, 27, 37, 1)",
border: "rgba(229, 229, 229, 0.2)",
selectedBorder: "rgba(255, 190, 92, 1)",
},
connection: {
background: "rgba(255, 255, 255, 0.5)",
selectedBackground: "rgba(234, 201, 74, 1)",
},
anchor: {
background: "rgba(255, 190, 92, 1)",
},
},
},
settings: {
canDragCamera: true,
canZoomCamera: true,
canCreateNewConnections: true,
useBezierConnections: true,
},
});
// Initialize blocks
React.useEffect(() => {
const blocks: TBlock[] = [
{
id: 'block1',
is: 'block',
name: 'Source',
x: 100,
y: 100,
width: 200,
height: 100,
selected: false,
anchors: [
{
id: 'out1',
type: 'OUT',
x: 200,
y: 50,
},
],
},
{
id: 'block2',
is: 'block',
name: 'Target',
x: 400,
y: 100,
width: 200,
height: 100,
selected: false,
anchors: [
{
id: 'in1',
type: 'IN',
x: 0,
y: 50,
},
],
},
];
setEntities({ blocks });
start();
graph.zoomTo("center", { padding: 100 });
}, []);
// Handle clicks on blocks
useGraphEvent(graph, "click", ({ target }) => {
console.log('Clicked block:', target);
});
// Render blocks
const renderBlock = useCallback((graph, block) => (
<BlockComponent graph={graph} block={block} />
), []);
return (
<div className="graph-container">
<GraphCanvas
graph={graph}
renderBlock={renderBlock}
className="custom-graph"
/>
</div>
);
}
// Required styles
const styles = `
.custom-graph {
width: 100%;
height: 100vh;
}
.custom-block {
background-color: var(--graph-block-bg);
border: 1px solid var(--graph-block-border);
border-radius: 4px;
}
.custom-block.selected {
border-color: var(--graph-block-border-selected);
}
.block-content {
padding: 16px;
color: white;
}
.anchor {
position: absolute;
width: 12px;
height: 12px;
background: var(--graph-block-anchor-bg);
border-radius: 50%;
cursor: pointer;
}
.anchor.selected {
border: 2px solid var(--graph-block-anchor-border-selected);
}
.anchor-label {
position: absolute;
top: -20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
}
`;This example demonstrates:
- Basic block and anchor setup
- Connection handling
- CSS variable usage for styling
- Proper TypeScript typing using built-in types
- Event handling
The graph will display:
- Two blocks with input/output anchors
- Interactive connection creation
- Proper styling and hover effects