Showing Execution Status
When a workflow is executing, the canvas overlays each node with a colour-coded status border. Custom node renderers must implement this overlay the same way as the built-ins — by reading nodeStatuses from designerModeStore.
Where Status Data Lives
The designerModeStore (injected by withNodeHooks as this.designerMode) holds a map of node execution statuses keyed by node ID. This is populated in real-time via SignalR events.
// DesignerModeStore shape (relevant fields)
interface DesignerModeStore {
isExecutionMode: boolean;
nodeStatuses: Record<string, NodeExecutionStatus>;
// ...
}
type NodeExecutionStatus =
| 'pending'
| 'running'
| 'completed'
| 'failed'
| 'skipped'
| 'suspended'
| 'cancelled';
Reading Status in Your Renderer
// In renderBody() of your BaseNode subclass
renderBody() {
const { id } = this.props;
const { isExecutionMode, nodeStatuses } = this.designerMode;
// Only show status overlay when a workflow is running
const status = isExecutionMode ? (nodeStatuses[id] ?? 'pending') : null;
return (
<>
{/* ...handles... */}
<div className={`node-wrapper-inner ${status ? `status-${status}` : ''}`}>
<div className="node-header">...</div>
<div className="node-body">...</div>
</div>
{/* Status border overlay */}
{status && (
<div className={`node-status-overlay status-border-${status}`} />
)}
</>
);
}
Status Colours
| Status | Badge | Border Color | Notes |
|---|---|---|---|
| pending | Default | #718096 (grey) | Not yet reached in execution |
| running | Pulsing | #60a5fa (blue, animated) | CSS keyframe pulse animation on the border |
| completed | Solid | #34d399 (green) | Execution succeeded |
| failed | Solid | #f87171 (red) | Unhandled error; execution routed to error path or stopped |
| skipped | Dimmed | #718096 (grey, dashed) | Branch not taken (e.g. false arm of an if node) |
| suspended | Pulsing | #fbbf24 (amber) | Waiting for human approval (HIL pattern) |
Shared CSS Classes
The shared Flow Studio CSS defines the overlay classes so you do not have to duplicate them:
/* From flow-studio-designer shared CSS */
.node-status-overlay {
position: absolute;
inset: 0;
border-radius: inherit;
pointer-events: none;
border: 2px solid transparent;
transition: border-color 0.2s;
}
.status-border-running {
border-color: #60a5fa;
animation: statusPulse 1.5s ease-in-out infinite;
}
.status-border-completed { border-color: #34d399; }
.status-border-failed { border-color: #f87171; }
.status-border-suspended {
border-color: #fbbf24;
animation: statusPulse 2s ease-in-out infinite;
}
.status-border-skipped { border-color: #718096; border-style: dashed; }
.status-border-pending { border-color: #718096; opacity: 0.4; }
@keyframes statusPulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
Execution Mode Guard
Always check isExecutionMode before reading nodeStatuses. In design mode (no running execution), the status map may contain stale data from the previous execution. Displaying stale status in design mode is confusing — gate the entire overlay behind the mode check.
// Correct pattern — guard with isExecutionMode
const status = this.designerMode.isExecutionMode
? (this.designerMode.nodeStatuses[this.props.id] ?? 'pending')
: null;
// Wrong — may show stale status from previous run
const status = this.designerMode.nodeStatuses[this.props.id];
CustomNode.tsx contains the canonical execution status overlay implementation. Copy the node-status-overlay div and the isExecutionMode guard directly — it is already tested and handles all six status values correctly.