Skip to content

Workflow Viewer

The WorkflowViewer component provides a read-only visualization of a workflow instance's current state.

Usage

import { WorkflowViewer } from '@apitomy/flow-ui';
import type { Workflow, WorkflowInstance } from '@apitomy/flow-ui';

function MyWorkflowViewer() {
  return (
    <WorkflowViewer
      workflow={workflowDefinition}
      instance={workflowInstance}
      theme="light"
    />
  );
}

Props

Prop Type Required Description
workflow Workflow Yes The workflow definition (graph structure)
instance WorkflowInstance Yes The runtime state to visualize
theme FlowTheme No 'light' or 'dark' (default: 'light'). Controls the color scheme of the viewer and React Flow canvas
nodeContextMenuItems WorkflowViewerNodeMenuItem[] \| ((nodeId: string) => WorkflowViewerNodeMenuItem[]) No Host-contributed actions for a node's right-click menu. See Node Context Menu

Features

Read-Only Canvas

The viewer displays the workflow graph but does not allow editing:

  • Nodes are not draggable
  • Edges cannot be created or removed
  • Nodes can be selected for inspection; selection does not edit the definition
  • Pan and zoom are available for navigation

Current Node Highlight

The node where the instance is currently positioned is highlighted so it is immediately obvious where the workflow is. While the instance is active (running or waiting), the current node also shows an animated "marching ants" ring around it.

The current node's color reflects the instance's status:

Instance status Current node styling
Running / Waiting Amber border with an animated ring
Failed Red border (ring stops)
Cancelled Muted/grey border (ring stops)
Completed Styled as a normal visited node (no ring)

Path Taken

The viewer styles nodes and edges based on the instance's execution history:

Element Visited Not Visited
Nodes Full opacity Dimmed (40% opacity)
Edges Green, thicker stroke Dimmed

Arrival edges for active branches animate with a flowing dash pattern. Terminal instances have no animated edges, even if their serialized snapshot still retains branch records.

Node details and repeat visits

Click a node to inspect its State or Definition in the resizable side panel. The default state view follows the latest visit as new history arrives; a visit picker lets you inspect earlier loop visits. Parallel history is grouped by branch. Clicking the canvas clears selection. Host right-click actions are independent of this built-in inspection.

The State view shows each visit's recorded Inputs and Outputs. For human-task nodes it also shows the task Title computed for that visit (the node name for history recorded before titles existed) and the task Description. Long values are shortened; use the expand button to open the full value in a dialog, where JSON is formatted and highlighted.

The viewer responds to new workflow and instance props. Replace changed objects/arrays rather than mutating them in place. Supply the definition that belongs to the instance: automatic version pinning is not implemented (see current contracts).

Parallel Branches

For instances that fork, the viewer highlights every currently-active node at once rather than a single cursor. The arrival edge of each active branch is animated, while all previously-traversed edges keep their static "taken" styling. When you open a node's detail, its visit history is grouped by branch, and a Branch row identifies which branch a given visit belongs to (the root branch is not labeled). At a terminal state — completed, failed, or cancelled — the viewer ignores branch activity and uses currentNodeId if present. A cancelled multi-branch instance may have no single cursor to highlight.

History-Based Rendering

The viewer reads the instance's history array to determine:

  • Which nodes have been visited (history[].nodeId)
  • Which edges have been followed (history[].edgeId)
  • Which nodes are active (instance.activeBranches, gated by status; terminal fallback uses currentNodeId)

Auto-Layout

If the workflow's nodes have no positions (or all overlap at the same coordinates), the viewer arranges them automatically using the same layered layout as the editor, so a definition built without explicit position values still renders cleanly. Layout is computed from the graph structure only, so it does not re-run as the instance state changes.

Node Context Menu

By default, right-clicking a node does nothing (the browser's own context menu is left untouched). A host can add its own actions to a node's right-click menu with the nodeContextMenuItems prop — for example "View logs" or "Open task in Jira".

Provide either a static array (the same items for every node) or a function that receives the nodeId and returns items tailored to that node. Returning an empty array means no menu is shown for that node.

import { WorkflowViewer } from '@apitomy/flow-ui';
import type { WorkflowViewerNodeMenuItem } from '@apitomy/flow-ui';

<WorkflowViewer
  workflow={workflow}
  instance={instance}
  nodeContextMenuItems={(nodeId) => [
    { id: 'logs', label: 'View logs', onSelect: (id) => openLogs(id) },
    { id: 'cancel', label: 'Cancel task', danger: true, onSelect: (id) => cancelTask(id) },
  ]}
/>

Each item has the following shape:

interface WorkflowViewerNodeMenuItem {
  id: string;                              // stable React key
  label: string;                           // menu text
  icon?: React.ReactNode;                  // optional leading icon
  danger?: boolean;                        // render as a destructive action
  onSelect: (nodeId: string) => void;      // invoked with the right-clicked node's id
}

Styling

The viewer requires the same CSS imports as the editor:

import '@patternfly/patternfly/patternfly.css';
import '@xyflow/react/dist/style.css';
import '@apitomy/flow-ui/style.css';

The viewer fills its container — ensure the parent element has explicit dimensions.

See Workflow Diff Viewer for definition comparisons and browser verification for live-update checks.