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 are not selectable
  • 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

The most recently followed edge is animated with a flowing dash pattern.

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 — no branches are active, so the viewer falls back to highlighting the final node.

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 node is current (instance.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.