Skip to main content
CrystalFlow provides individual components that can be composed to build custom workflow UIs.

NodePalette

Sidebar component displaying available node types.
function NodePalette(props: NodePaletteProps): JSX.Element;
Props:
nodeTypes
Array<typeof Node>
Array of node classes to display.
onNodeSelect
(nodeClass: typeof Node) => void
Callback when a node is selected/added.
searchable
boolean
default:"true"
Show search input to filter nodes.
groupByCategory
boolean
default:"true"
Group nodes by category.
Example:
import { NodePalette } from '@crystalflow/react';
import { AddNode, MultiplyNode } from './nodes';

<NodePalette
  nodeTypes={[AddNode, MultiplyNode]}
  onNodeSelect={(NodeClass) => {
    console.log('Selected:', NodeClass);
  }}
  searchable={true}
  groupByCategory={true}
/>

PropertyPanel

Sidebar component for editing node properties.
function PropertyPanel(props: PropertyPanelProps): JSX.Element;
Props:
selectedNode
Node | null
Currently selected node to display properties for.
onPropertyChange
(nodeId: string, property: string, value: any) => void
Callback when a property value changes.
Example:
import { PropertyPanel } from '@crystalflow/react';

<PropertyPanel
  selectedNode={selectedNode}
  onPropertyChange={(nodeId, property, value) => {
    console.log(`Node ${nodeId}.${property} = ${value}`);
    updateNodeProperty(nodeId, property, value);
  }}
/>

WorkflowCanvas

React Flow canvas component for rendering the workflow.
function WorkflowCanvas(props: WorkflowCanvasProps): JSX.Element;
Props:
workflow
Workflow
Workflow to render.
onNodeClick
(node: Node) => void
Callback when a node is clicked.
onCanvasClick
() => void
Callback when empty canvas area is clicked.
Example:
import { WorkflowCanvas } from '@crystalflow/react';

<WorkflowCanvas
  workflow={workflow}
  onNodeClick={(node) => {
    console.log('Clicked node:', node.id);
    setSelectedNode(node);
  }}
  onCanvasClick={() => {
    setSelectedNode(null);
  }}
/>

Toolbar

Toolbar component with common actions.
function Toolbar(props: ToolbarProps): JSX.Element;
Props:
onExecute
() => void
Execute button callback.
onSave
() => void
Save button callback.
onLoad
(file: File) => void
Load button callback.
onClear
() => void
Clear button callback.
isExecuting
boolean
Whether workflow is currently executing.
Example:
import { Toolbar } from '@crystalflow/react';

<Toolbar
  onExecute={handleExecute}
  onSave={handleSave}
  onLoad={handleLoad}
  onClear={handleClear}
  isExecuting={isExecuting}
/>

CustomNode

Custom React Flow node renderer.
function CustomNode(props: NodeProps): JSX.Element;
Props:
data
NodeData
Node data including metadata and values.
selected
boolean
Whether node is selected.
This component is automatically used by WorkflowCanvas and typically doesn’t need to be used directly.

ExecutionProgress

Component showing workflow execution progress.
Work in Progress: This component is currently being implemented.
import { ExecutionProgress } from '@crystalflow/react';

<ExecutionProgress
  isExecuting={isExecuting}
  progress={progress}
  currentNode={currentNode}
/>

ErrorDisplay

Component for displaying execution errors.
Work in Progress: This component is currently being implemented.
import { ErrorDisplay } from '@crystalflow/react';

<ErrorDisplay error={error} onDismiss={() => setError(null)} />

Complete Custom UI Example

Build a custom workflow UI by composing components:
import React, { useState } from 'react';
import {
  NodePalette,
  PropertyPanel,
  WorkflowCanvas,
  Toolbar,
  useWorkflow,
  useExecution
} from '@crystalflow/react';
import { AddNode, MultiplyNode, DisplayNode } from './nodes';

function CustomWorkflowUI() {
  const [selectedNode, setSelectedNode] = useState(null);
  const { workflow, addNode, updateNodeData, clear } = useWorkflow();
  const { execute, isExecuting, result } = useExecution();

  const nodes = [AddNode, MultiplyNode, DisplayNode];

  const handleNodeSelect = (NodeClass) => {
    const node = addNode(NodeClass, {
      position: { x: 300, y: 300 }
    });
    setSelectedNode(node);
  };

  const handlePropertyChange = (nodeId, property, value) => {
    updateNodeData(nodeId, { [property]: value });
  };

  const handleExecute = async () => {
    await execute(workflow);
  };

  return (
    <div className="custom-ui">
      <Toolbar
        onExecute={handleExecute}
        onClear={clear}
        isExecuting={isExecuting}
      />

      <div className="main-area">
        <NodePalette
          nodeTypes={nodes}
          onNodeSelect={handleNodeSelect}
        />

        <WorkflowCanvas
          workflow={workflow}
          onNodeClick={setSelectedNode}
          onCanvasClick={() => setSelectedNode(null)}
        />

        <PropertyPanel
          selectedNode={selectedNode}
          onPropertyChange={handlePropertyChange}
        />
      </div>

      {result && (
        <div className="results">
          <h3>Execution Result</h3>
          <pre>{JSON.stringify(result, null, 2)}</pre>
        </div>
      )}
    </div>
  );
}

export default CustomWorkflowUI;

Styling Components

All components accept className and style props:
<NodePalette
  nodeTypes={nodes}
  onNodeSelect={handleSelect}
  className="my-palette"
  style={{ width: '250px', backgroundColor: '#f5f5f5' }}
/>