Skip to main content
The CrystalFlow React package provides components to create visual workflow builder interfaces powered by React Flow.

Overview

The @crystalflow/react package includes:

WorkflowBuilder

Complete workflow editor component

Custom Hooks

React hooks for workflow management

Node Components

Customizable node renderers

Panels & Controls

Property panels and toolbars

Installation

npm install @crystalflow/react @crystalflow/core reflect-metadata react react-dom

Basic Usage

App.tsx
import React from 'react';
import { WorkflowBuilder } from '@crystalflow/react';
import { AddNode, MultiplyNode } from './nodes';

function App() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <WorkflowBuilder
        nodes={[AddNode, MultiplyNode]}
        onWorkflowChange={(workflow) => {
          console.log('Workflow updated:', workflow);
        }}
      />
    </div>
  );
}

export default App;

WorkflowBuilder Props

nodes
NodeClass[]
required
Array of node classes to make available in the palette
initialWorkflow
Workflow
Optional initial workflow to load
onWorkflowChange
(workflow: Workflow) => void
Callback when workflow changes
onExecute
(result: ExecutionResult) => void
Callback when workflow executes
showToolbar
boolean
default:"true"
Show/hide the toolbar
showNodePalette
boolean
default:"true"
Show/hide the node palette
showPropertyPanel
boolean
default:"true"
Show/hide the property panel

Complete Example

import React, { useState } from 'react';
import { WorkflowBuilder } from '@crystalflow/react';
import { 
  NumberInputNode,
  AddNode,
  MultiplyNode,
  DisplayNode 
} from './nodes';

function App() {
  const [workflow, setWorkflow] = useState(null);

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <WorkflowBuilder
        nodes={[
          NumberInputNode,
          AddNode,
          MultiplyNode,
          DisplayNode
        ]}
        onWorkflowChange={(wf) => {
          setWorkflow(wf);
          console.log('Workflow updated');
        }}
        onExecute={(result) => {
          console.log('Execution result:', result);
          if (result.status === 'success') {
            alert('Workflow executed successfully!');
          } else {
            alert(`Execution failed: ${result.error?.message}`);
          }
        }}
        showToolbar={true}
        showNodePalette={true}
        showPropertyPanel={true}
      />
    </div>
  );
}

export default App;

Using Custom Hooks

For more control, use the underlying hooks:
import React from 'react';
import { 
  useWorkflow, 
  useNodeRegistry,
  useExecution 
} from '@crystalflow/react';

function CustomWorkflowEditor() {
  const { workflow, addNode, removeNode, connect } = useWorkflow();
  const { registry } = useNodeRegistry([AddNode, MultiplyNode]);
  const { execute, isExecuting, result } = useExecution();

  const handleAddNode = (nodeType: string) => {
    addNode(nodeType, {
      position: { x: 100, y: 100 }
    });
  };

  const handleExecute = async () => {
    const executionResult = await execute(workflow);
    console.log('Result:', executionResult);
  };

  return (
    <div>
      <button onClick={() => handleAddNode('math.add')}>
        Add Math Node
      </button>
      <button onClick={handleExecute} disabled={isExecuting}>
        {isExecuting ? 'Executing...' : 'Execute Workflow'}
      </button>
      {/* Render workflow canvas */}
    </div>
  );
}

Node Palette

The node palette allows users to drag and drop nodes:
import { NodePalette } from '@crystalflow/react';

<NodePalette
  nodes={[AddNode, MultiplyNode]}
  onNodeDrop={(nodeType, position) => {
    workflow.addNodeByType(nodeType, { position });
  }}
/>

Property Panel

The property panel displays node properties and inputs:
import { PropertyPanel } from '@crystalflow/react';

<PropertyPanel
  node={selectedNode}
  onChange={(propertyName, value) => {
    selectedNode.setInputValue(propertyName, value);
  }}
/>

Toolbar Actions

Work in Progress: Some toolbar features are still under development.
import { Toolbar } from '@crystalflow/react';

<Toolbar
  onSave={() => {
    const json = serializeWorkflow(workflow.toJSON());
    // Save to file/API
  }}
  onLoad={() => {
    // Load from file/API
  }}
  onExecute={() => {
    // Execute workflow
  }}
  onClear={() => {
    // Clear workflow
  }}
/>

Customizing Appearance

Custom Node Renderer

import { CustomNode } from '@crystalflow/react';

<WorkflowBuilder
  nodeRenderer={(node) => (
    <CustomNode
      node={node}
      className="my-custom-node"
      style={{
        background: '#f0f0f0',
        border: '2px solid #333'
      }}
    />
  )}
  nodes={[AddNode]}
/>

Styling

styles.css
.crystalflow-node {
  background: white;
  border: 2px solid #3b82f6;
  border-radius: 8px;
  padding: 16px;
}

.crystalflow-node-header {
  font-weight: bold;
  margin-bottom: 12px;
}

.crystalflow-input-handle {
  background: #10b981;
}

.crystalflow-output-handle {
  background: #ef4444;
}

React Flow Integration

WorkflowBuilder is built on React Flow. Access React Flow features:
import { ReactFlowProvider } from 'reactflow';
import { WorkflowBuilder } from '@crystalflow/react';

<ReactFlowProvider>
  <WorkflowBuilder nodes={[AddNode]} />
</ReactFlowProvider>

Event Handling

Handle workflow events:
<WorkflowBuilder
  nodes={[AddNode]}
  onNodeAdd={(node) => console.log('Node added:', node)}
  onNodeRemove={(nodeId) => console.log('Node removed:', nodeId)}
  onConnectionCreate={(conn) => console.log('Connected:', conn)}
  onConnectionRemove={(conn) => console.log('Disconnected:', conn)}
  onNodeSelect={(node) => console.log('Node selected:', node)}
/>

Keyboard Shortcuts

Work in Progress: Keyboard shortcuts are planned for a future release.
Planned keyboard shortcuts:
  • Delete - Remove selected nodes
  • Ctrl/Cmd + Z - Undo
  • Ctrl/Cmd + Shift + Z - Redo
  • Ctrl/Cmd + S - Save workflow
  • Ctrl/Cmd + E - Execute workflow

Persistence

Save and load workflows:
import { serializeWorkflow, deserializeWorkflow } from '@crystalflow/core';

// Save
const saveWorkflow = () => {
  const json = serializeWorkflow(workflow.toJSON());
  localStorage.setItem('workflow', json);
};

// Load
const loadWorkflow = () => {
  const json = localStorage.getItem('workflow');
  if (json) {
    const workflowData = deserializeWorkflow(json);
    const loadedWorkflow = Workflow.fromJSON(workflowData);
    // Set workflow in UI
  }
};

Monaco Editor Integration

Work in Progress: Monaco JSON editor integration is under development for dual-mode editing (visual + code).
Planned features:
  • Side-by-side visual and JSON editing
  • Real-time sync between modes
  • JSON schema validation
  • Syntax highlighting

Best Practices

Use descriptive labels and categories to help users find nodes.
Show user-friendly error messages when execution fails.
Implement auto-save to prevent data loss.
Validate workflows before execution and show validation errors.
Show execution progress and node states during workflow execution.

Example Projects

Check out complete example projects in the repository:
  • Simple Math Workflow - Basic arithmetic operations
  • Data Processing Pipeline - Fetch, filter, transform data
  • API Integration - HTTP requests and response handling

Next Steps