---
name: fbp-graph-editor
description: Houdini-inspired graph editor for Flow-Based Programming built with React. Use when building or customizing a visual graph editor, working with the @fbp/graph-editor package.
---
Houdini-inspired graph editor for Flow-Based Programming built with React.
## Installation
```bash
pnpm add @fbp/graph-editor
```
## Overview
`@fbp/graph-editor` is a React component for visual editing of FBP graphs. It features an SVG-based canvas with pan/zoom, node rendering with fully-qualified type paths, Bezier edge connections, selection system, properties panel, and subgraph navigation.
## Basic Usage
```tsx
import { GraphEditor } from '@fbp/graph-editor';
import type { Graph, NodeDefinition } from '@fbp/types';
const graph: Graph = {
name: 'my-graph',
definitions: [
{
context: 'js',
category: 'math',
type: 'js/math/add',
inputs: [
{ name: 'a', type: 'number' },
{ name: 'b', type: 'number' }
],
outputs: [
{ name: 'sum', type: 'number' }
]
}
],
nodes: [
{ name: 'add1', type: 'js/math/add', meta: { x: 100, y: 100 } }
],
edges: []
};
function App() {
return (
);
}
```
## Features
### SVG-Based Canvas
The editor uses SVG for rendering, providing crisp visuals at any zoom level. Pan and zoom are supported via mouse/trackpad gestures.
### Node Rendering
Nodes display their fully-qualified type paths (e.g., `js/math/add`) and show input/output ports based on their definition.
### Bezier Edge Connections
Edges are rendered as smooth Bezier curves connecting output ports to input ports. Click and drag from a port to create new connections.
### Selection System
- Click to select a single node
- Shift+click to add/remove from selection
- Box select with Shift+drag
- Cmd/Ctrl+A to select all
- Escape to clear selection
- Cmd/Ctrl+D to duplicate selection
### Properties Panel
Auto-generated from `PropDefinition`. When a node is selected, its properties are displayed in a panel for editing.
### Subgraph Navigation
- Enter to dive into a selected subnet
- U to go up from a subnet
## Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| Delete/Backspace | Delete selected nodes/edges |
| Cmd/Ctrl+D | Duplicate selection |
| Cmd/Ctrl+A | Select all |
| Escape | Clear selection |
| Enter | Dive into selected subnet |
| U | Go up from subnet |
| Shift+Drag | Box select |
| Alt+Drag or Middle Mouse | Pan canvas |
| Ctrl/Cmd+Scroll | Zoom |
## Channel Reference Detection
The editor detects channel references in property values using patterns like `ch("...")` and `$VAR`, enabling visual feedback for connected parameters.
## Styling
The editor uses Tailwind CSS for styling. Ensure your project has Tailwind configured:
```tsx
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx}',
'./node_modules/@fbp/graph-editor/**/*.{js,ts,jsx,tsx}'
],
// ...
};
```
## Props
```typescript
interface GraphEditorProps {
graph: Graph;
onChange?: (graph: Graph) => void;
definitions?: NodeDefinition[];
readOnly?: boolean;
}
```
| Prop | Type | Description |
|------|------|-------------|
| `graph` | `Graph` | The graph to display and edit |
| `onChange` | `(graph: Graph) => void` | Callback when graph changes |
| `definitions` | `NodeDefinition[]` | Additional node type definitions |
| `readOnly` | `boolean` | Disable editing |
## Peer Dependencies
```json
{
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
}
```
## Example: Complete Editor Setup
```tsx
import { useState } from 'react';
import { GraphEditor } from '@fbp/graph-editor';
import type { Graph } from '@fbp/types';
const initialGraph: Graph = {
name: 'calculator',
definitions: [
{
type: 'js/const/number',
context: 'js',
category: 'const',
props: [{ name: 'value', type: 'number', default: 0 }],
outputs: [{ name: 'value', type: 'number' }]
},
{
type: 'js/math/add',
context: 'js',
category: 'math',
inputs: [
{ name: 'a', type: 'number' },
{ name: 'b', type: 'number' }
],
outputs: [{ name: 'sum', type: 'number' }]
}
],
nodes: [
{ name: 'num1', type: 'js/const/number', meta: { x: 50, y: 50 }, props: [{ name: 'value', value: 5 }] },
{ name: 'num2', type: 'js/const/number', meta: { x: 50, y: 150 }, props: [{ name: 'value', value: 3 }] },
{ name: 'add', type: 'js/math/add', meta: { x: 250, y: 100 } }
],
edges: [
{ src: { node: 'num1', port: 'value' }, dst: { node: 'add', port: 'a' } },
{ src: { node: 'num2', port: 'value' }, dst: { node: 'add', port: 'b' } }
]
};
function App() {
const [graph, setGraph] = useState(initialGraph);
return (
);
}
```