Canvas
ReactFlow surface for visual workflows and agent graphs. Pre-configured for pan-on-scroll, drag selection and Backspace / Delete deletion.
Overview
Canvas is a thin Gremorie wrapper around ReactFlow from @xyflow/react. It sets the defaults Gremorie expects for AI workflow canvases: panOnScroll, selectionOnDrag, fitView on first render, deletion bound to Backspace and Delete, and a sidebar-toned Background.
Use it for any node-and-edge surface: agent flow editors, prompt graph visualizations, pipeline builders, multi-step LLM routing. Compose with Node, Edge, Connection, Controls, Panel and Toolbar from the same family.
The xyflow stylesheet is imported at the module level (@xyflow/react/dist/style.css), so consuming projects do not need to manage it manually - though importing it once at the app entry is still recommended to avoid order-of-execution surprises.
Preview
'use client';import '@xyflow/react/dist/style.css';import { Canvas, Controls, Panel } from '@gremorie/rx-ai';import { type Edge as FlowEdge, type Node as FlowNode, ReactFlowProvider,} from '@xyflow/react';const canvasNodes: FlowNode[] = [ { id: '1', position: { x: 0, y: 0 }, data: { label: 'Prompt' }, type: 'input', }, { id: '2', position: { x: 220, y: -40 }, data: { label: 'Retrieve context' }, }, { id: '3', position: { x: 220, y: 80 }, data: { label: 'Generate answer' }, type: 'output', },];const canvasEdges: FlowEdge[] = [ { id: 'e1-2', source: '1', target: '2' }, { id: 'e1-3', source: '1', target: '3' },];export function CanvasPreview() { return ( <div className="h-[360px] w-full"> <ReactFlowProvider> <Canvas edges={canvasEdges} nodes={canvasNodes}> <Controls /> <Panel position="top-right"> <span className="px-2 text-muted-foreground text-xs"> 3 nodes · 2 edges </span> </Panel> </Canvas> </ReactFlowProvider> </div> );}Anatomy
Canvas React Flow viewport
├─ Node … nodeTypes (custom renderers)
├─ Edge … edgeTypes (Edge.Temporary · Edge.Animated)
├─ Connection in-progress connection line
└─ overlays (children) Panel · Controls · ToolbarInstallation
bash npx gremorie@latest add rx-canvas bash pnpm dlx gremorie@latest add rx-canvas bash yarn dlx gremorie@latest add rx-canvas bash bunx --bun gremorie@latest add rx-canvas Requires @xyflow/react as a peer dependency.
Usage
import { useNodesState, useEdgesState } from "@xyflow/react";
import {
Canvas,
Controls,
Panel,
Node,
NodeContent,
NodeHeader,
NodeTitle,
} from "@gremorie/rx-ai";
const nodeTypes = { card: (props) => (
<Node {...props} handles={{ target: true, source: true }}>
<NodeHeader>
<NodeTitle>{props.data.label}</NodeTitle>
</NodeHeader>
<NodeContent>{props.data.body}</NodeContent>
</Node>
)};
export function Workflow() {
const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, , onEdgesChange] = useEdgesState(initialEdges);
return (
<div className="h-[480px] w-full">
<Canvas
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
>
<Controls />
<Panel position="top-right">Workflow tools</Panel>
</Canvas>
</div>
);
}
The Angular edition of this component is planned; the React edition is production-ready today.
API
<Canvas>
Forwards all ReactFlowProps from @xyflow/react. The Gremorie wrapper sets these defaults (any prop you pass overrides them):
| Default | Value | Why |
|---|---|---|
deleteKeyCode | ["Backspace", "Delete"] | Both Mac and Windows users can delete the selection. |
fitView | true | On mount, zoom and pan so all nodes are visible. |
panOnDrag | false | Dragging the canvas selects instead of panning. |
panOnScroll | true | Two-finger trackpad / wheel scroll pans the view. |
selectionOnDrag | true | Drag from empty space draws a selection box. |
zoomOnDoubleClick | false | Double-click is reserved for node-level actions. |
Renders a Background with bgColor="var(--sidebar)" so the canvas matches the surrounding shell.
children are rendered inside the ReactFlow. Use this slot for Controls, Panel, MiniMap, etc.
Composition
<Canvas>owns the surface. Passnodes,edges,nodeTypes,edgeTypesand the change handlers from xyflow.- Custom node types are React components that render
Nodefrom@gremorie/rx-aiwith its handles. <Controls>,<Panel>,<Toolbar>are children that overlay the canvas.<Connection>is the line that follows the cursor while the user drags from a handle - pass it via the xyflowconnectionLineComponentprop.
Variations
Read-only viewer
For visualizing a static flow (e.g. an agent trace), disable interactions.
<Canvas
nodes={trace.nodes}
edges={trace.edges}
nodesDraggable={false}
nodesConnectable={false}
elementsSelectable={false}
>
<Controls showInteractive={false} />
</Canvas>Editor with custom connection line
Pair with Connection for a Gremorie-themed connection preview.
import { Connection } from '@gremorie/rx-ai';
<Canvas
nodes={nodes}
edges={edges}
connectionLineComponent={Connection}
onConnect={onConnect}
>
<Controls />
</Canvas>;With minimap and panel overlay
import { MiniMap } from '@xyflow/react';
<Canvas nodes={nodes} edges={edges}>
<Controls />
<Panel position="top-left">
<p className="text-xs font-medium">Agent graph</p>
</Panel>
<MiniMap pannable zoomable />
</Canvas>;Accessibility
- Keyboard: xyflow ships a documented keyboard model - Tab focuses the canvas, then arrow keys nudge selected nodes; Backspace / Delete remove them. Selection box still requires pointer input. For full keyboard parity, supply
Panel-level shortcuts. - Focus visible: xyflow exposes its own focus ring on the canvas. Do not override
outline: noneon the root. - Reduced motion: Pan and zoom animations honor
prefers-reduced-motionvia xyflow's internal handling. Custom edge animations (e.g.Connection'sanimatedclass) should be wrapped in your own@media (prefers-reduced-motion: reduce)overrides. - Screen readers: Nodes are rendered as plain DOM. Ensure each node's content has meaningful text (not just icons), and add
aria-labelto interactive children insideNode.