CrystalFlow includes built-in nodes for common workflow patterns. These nodes are production-ready and fully tested.
Flow Control Nodes
IfNode
Classic if/else conditional branching.
import { IfNode } from '@crystalflow/core' ;
Type: 'flow.if'
Category: 'Flow Control'
Implements: IConditionalNode
Boolean condition that determines which branch to execute.
true → routes to thenOutput
false → routes to elseOutput
Optional data to pass through to the active branch. The value is set on either
thenOutput or elseOutput depending on the condition.
Outputs
Output for the ‘then’ branch (when condition is true). Contains the passthrough
value if provided, otherwise undefined.
Output for the ‘else’ branch (when condition is false). Contains the passthrough
value if provided, otherwise undefined.
Methods
Sets the active output (thenOutput or elseOutput) based on the condition value.
The inactive output is set to undefined.
evaluateCondition (): string
Returns 'thenOutput' if condition is true, 'elseOutput' if false.
Throws: Error if condition is not a boolean value.
Example
import { Workflow , IfNode , BooleanInputNode , DisplayNode } from '@crystalflow/core' ;
const workflow = new Workflow ();
// Create nodes
const boolInput = workflow . addNode ( BooleanInputNode );
const ifNode = workflow . addNode ( IfNode );
const thenDisplay = workflow . addNode ( DisplayNode );
const elseDisplay = workflow . addNode ( DisplayNode );
// Connect
workflow . connect ( boolInput . id , 'value' , ifNode . id , 'condition' );
workflow . connect ( boolInput . id , 'value' , ifNode . id , 'value' );
workflow . connect ( ifNode . id , 'thenOutput' , thenDisplay . id , 'message' );
workflow . connect ( ifNode . id , 'elseOutput' , elseDisplay . id , 'message' );
// Execute
await workflow . execute ();
// If condition = true: only thenDisplay executes
// If condition = false: only elseDisplay executes
Usage in WorkflowBuilder
import { WorkflowBuilder } from '@crystalflow/react' ;
import { IfNode } from '@crystalflow/core' ;
< WorkflowBuilder
nodes = { [ IfNode , /* other nodes */ ] }
/>
The IfNode appears in the “Flow Control” category of the node palette.
SwitchNode
Multi-way branching with case matching (switch/case pattern).
import { SwitchNode } from '@crystalflow/core' ;
Type: 'flow.switch'
Category: 'Flow Control'
Implements: IConditionalNode
Value to match against case values. Compared using strict equality (===).
Optional data to pass through to the matching branch. The data is set on the
matching case output or default output.
Properties
Array of case values to match against. The number of cases determines how many
output ports are created (case_0, case_1, …, case_N, default). Dynamic Outputs: When cases are added or removed, the node automatically
updates its output ports.
Outputs
Dynamic: Outputs are generated based on the cases property.
case_0, case_1, ..., case_N
One output for each case in the cases array. The label shows the actual case
value (e.g., if cases[0] is ‘apple’, the label is ‘apple’).
Default output used when no case matches. Always present regardless of the
number of cases.
Methods
getOutputs (): PortDefinition []
Dynamically generates output port definitions based on the cases array.
Clears all outputs, then finds the matching case and sets only that output
(or the default output if no match).
evaluateCondition (): string
Returns 'case_X' where X is the index of the matching case, or 'default'
if no case matches.
Example
import { Workflow , SwitchNode , StringInputNode , DisplayNode } from '@crystalflow/core' ;
const workflow = new Workflow ();
// Create switch node with 3 cases
const statusInput = workflow . addNode ( StringInputNode );
const switchNode = workflow . addNode ( SwitchNode );
switchNode . setProperty ( 'cases' , [ 'pending' , 'approved' , 'rejected' ]);
// Create handlers
const pendingHandler = workflow . addNode ( DisplayNode );
const approvedHandler = workflow . addNode ( DisplayNode );
const rejectedHandler = workflow . addNode ( DisplayNode );
const defaultHandler = workflow . addNode ( DisplayNode );
// Connect
workflow . connect ( statusInput . id , 'value' , switchNode . id , 'value' );
workflow . connect ( statusInput . id , 'value' , switchNode . id , 'data' );
workflow . connect ( switchNode . id , 'case_0' , pendingHandler . id , 'message' ); // 'pending'
workflow . connect ( switchNode . id , 'case_1' , approvedHandler . id , 'message' ); // 'approved'
workflow . connect ( switchNode . id , 'case_2' , rejectedHandler . id , 'message' ); // 'rejected'
workflow . connect ( switchNode . id , 'default' , defaultHandler . id , 'message' );
// Execute with value = 'approved'
await workflow . execute ();
// Result: Only approvedHandler executes
Dynamic Outputs Example
const switchNode = new SwitchNode ();
// Start with 2 cases
switchNode . setProperty ( 'cases' , [ 'option1' , 'option2' ]);
console . log ( switchNode . getOutputs ());
// [
// { id: 'case_0', label: 'option1', type: 'any' },
// { id: 'case_1', label: 'option2', type: 'any' },
// { id: 'default', label: 'Default', type: 'any' }
// ]
// Add a third case
switchNode . setProperty ( 'cases' , [ 'option1' , 'option2' , 'option3' ]);
console . log ( switchNode . getOutputs ());
// [
// { id: 'case_0', label: 'option1', type: 'any' },
// { id: 'case_1', label: 'option2', type: 'any' },
// { id: 'case_2', label: 'option3', type: 'any' },
// { id: 'default', label: 'Default', type: 'any' }
// ]
Usage in WorkflowBuilder
import { WorkflowBuilder } from '@crystalflow/react' ;
import { SwitchNode } from '@crystalflow/core' ;
< WorkflowBuilder
nodes = { [ SwitchNode , /* other nodes */ ] }
/>
The SwitchNode appears in the “Flow Control” category. The property panel shows
an array editor for the cases property, allowing you to add/remove cases
interactively. Outputs update in real-time as cases change.
Comparison
Feature IfNode SwitchNode Branches 2 (then/else) N + 1 (cases + default) Condition Type Boolean Any (strict equality) Dynamic Outputs No Yes Use Case Simple binary decisions Multi-way routing Example Valid/Invalid, Yes/No Status routing, Type routing
Best Practices
Use IfNode for Binary Decisions
For simple true/false or valid/invalid scenarios, IfNode is clearer and
more efficient than SwitchNode with 2 cases.
Use SwitchNode for Multi-Way Routing
When you have 3+ possible values to route on, SwitchNode is more maintainable
than nested IfNodes.
Always Handle Default Case
Connect a handler to the default output of SwitchNode to gracefully handle
unexpected values.
SwitchNode uses strict equality (===). Ensure your values and cases are the
same type (‘1’ !== 1).