Docs
Customization

Custom Nodes, Edges & Theme Customization

Learn how to project custom Angular components into nodes, style connection handles, customize SVG edge paths, and override theme tokens.

1. Custom Node Angular Templates

You can create custom Angular components to render inside nodes. Use <ngx-workflow-handle> to declare input/output connection ports:

TypeScript
import { Component, input } from '@angular/core';
import { HandleComponent } from 'ngx-workflow';

@Component({
  selector: 'app-custom-card-node',
  standalone: true,
  imports: [HandleComponent],
  template: `
    <div class="custom-card">
      <!-- Input Connection Port -->
      <ngx-workflow-handle type="target" position="left" id="input-port" />

      <div class="card-header">
        <span class="status-dot"></span>
        <h4>Custom Card Node Title</h4>
      </div>

      <div class="card-body">
        <p>Custom Angular signal data bindings</p>
      </div>

      <!-- Output Connection Port -->
      <ngx-workflow-handle type="source" position="right" id="output-port" />
    </div>
  `
})
export class CustomCardNodeComponent {}

2. Connection Limits on Ports

Cap edges per port on default or custom nodes. Number values on isConnectable also act as a max connection count.

TypeScript
<ngx-workflow-diagram
  [nodes]="nodes()"
  [edges]="edges()"
  [maxConnectionsPerHandle]="2"
/>

nodes = signal<Node[]>([
  {
    id: 'a',
    position: { x: 80, y: 100 },
    ports: 4,
    maxConnectionsPerPort: 1,
    handleConfig: {
      bottom: { maxConnections: 3 }
    }
  }
]);

3. Direction-aware edge routing

Bezier, step, and smoothstep paths respect sourceHandle / targetHandle (top · right · bottom · left). A vertical bottom → top link curves out of the bottom port and into the top port — not a flat left-to-right diagonal.

TypeScript
edges = signal<Edge[]>([
  {
    id: 'e1',
    source: 'top-node',
    target: 'bottom-node',
    sourceHandle: 'bottom',
    targetHandle: 'top',
    type: 'bezier'
  }
]);

4. Custom Edge Styling & Animations

Stroke, labels, and animation dots accept hex / rgb() / rgba(). Built-in markers (arrow, arrowclosed, dot) match the edge stroke color. When animated is true and animationType is omitted, flow animation is used.

TypeScript
edges = signal<Edge[]>([
  {
    id: 'e-styled',
    source: 'n1',
    target: 'n2',
    animated: true,
    animationType: 'both',
    animationStyle: { fill: 'rgba(56, 189, 248, 0.9)' },
    markerEnd: 'arrowclosed',
    style: {
      stroke: 'rgba(56, 189, 248, 0.85)',
      strokeWidth: '3',
      strokeDasharray: '5,5'
    }
  }
]);

5. Node Colors (RGBA)

Set fill, text, and border via style / borderColor, or edit them in the properties sidebar RGBA pickers (swatch + opacity + rgba() text).

TypeScript
nodes = signal<Node[]>([
  {
    id: 'n1',
    position: { x: 80, y: 100 },
    label: 'Opaque',
    style: {
      backgroundColor: '#ffffff',
      color: '#0f172a',
      borderColor: '#94a3b8'
    }
  },
  {
    id: 'n2',
    position: { x: 320, y: 100 },
    label: 'Glass',
    style: {
      backgroundColor: 'rgba(15, 23, 42, 0.65)',
      color: 'rgba(248, 250, 252, 0.95)',
      borderColor: 'rgba(45, 212, 191, 0.8)'
    },
    borderColor: 'rgba(45, 212, 191, 0.8)'
  }
]);

6. CSS Theme Customization Tokens

Override default CSS variables in your application stylesheet to match your corporate design system:

CSS
:root {
  /* Brand Accent Colors */
  --ngx-workflow-accent: #3b82f6;
  --ngx-workflow-primary: #2dd4bf;
  --ngx-workflow-node-bg: #1e293b;
  --ngx-workflow-node-border: #334155;
  --ngx-workflow-node-text: #f8fafc;
  --ngx-workflow-edge-stroke: #94a3b8;

  /* Handle Ports */
  --ngx-workflow-handle-color: #60a5fa;
  --ngx-workflow-handle-hover: #2563eb;
  
  /* Selected Node Border Glow */
  --ngx-workflow-selected-glow: 0 0 0 2px #3b82f6;
}

7. Overlay Panels & Workflow Legends

Use <ngx-workflow-panel> inside the diagram container to project floating legends, controls, or annotations. Supports 9 viewport anchor positions (top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right) and inline dynamic [style].

HTML & TypeScript
<ngx-workflow-diagram [nodes]="nodes" [edges]="edges">
  <!-- Projected Legend Panel with 9-point positioning -->
  <ngx-workflow-panel
    [position]="'top-right'"
    [style]="{ minWidth: '280px', background: 'rgba(15, 23, 42, 0.94)', color: '#f8fafc' }"
  >
    <div class="legend-card glass-panel">
      <div class="legend-header">
        <h4>Workflow Legend</h4>
      </div>

      <div class="legend-section">
        <span class="legend-title">Node Status</span>
        <div class="legend-item"><span class="dot bg-blue"></span> Active Ingestion</div>
        <div class="legend-item"><span class="dot bg-green"></span> Database Sink</div>
        <div class="legend-item"><span class="dot bg-red"></span> Alert Dead-Letter</div>
      </div>

      <div class="legend-section">
        <span class="legend-title">Connections</span>
        <div class="legend-item"><span class="line line-solid"></span> Primary Flow</div>
        <div class="legend-item"><span class="line line-dashed"></span> Fallback Queue</div>
      </div>
    </div>
  </ngx-workflow-panel>
</ngx-workflow-diagram>

8. Headless Node Double-Click & REST API Inspector

Disable the default built-in properties editing sidebar ([showPropertiesSidebar]="false") and handle (nodeDoubleClick) with your custom projected API inspector panel or drawer.

HTML & TypeScript
<ngx-workflow-diagram
  [nodes]="nodes()"
  [edges]="edges()"
  [showPropertiesSidebar]="false"
  (nodeDoubleClick)="onNodeDoubleClick($event)"
  (paneClick)="closeInspector()"
>
  @if (inspectorOpen()) {
    <ngx-workflow-panel [position]="'center-right'" [style]="{ zIndex: 30 }">
      <div class="inspector-card glass-panel">
        <h4>{{ activeNode()?.label }} REST API Schema</h4>
        <p><strong>Endpoint:</strong> {{ nodeConfig()?.endpoint }}</p>
        <p><strong>Throughput:</strong> {{ nodeConfig()?.throughput }}</p>
        <button (click)="saveAndSync()">Save & Sync API</button>
      </div>
    </ngx-workflow-panel>
  }
</ngx-workflow-diagram>

9. Custom Zoom Controls Toolbar & Slots

Fully configure the zoom controls toolbar using [zoomControlsConfig]. Customize anchor positions (bottom-left, bottom-right, top-left, top-right), reorder buttons, or add custom action buttons and separators:

TypeScript
import { ZoomControlsConfig } from 'ngx-workflow';

const zoomConfig: ZoomControlsConfig = {
  position: 'bottom-left',
  items: [
    { id: 'zoomIn', type: 'action', action: 'zoomIn', icon: 'plus', title: 'Zoom In' },
    { id: 'zoomPercent', type: 'view', view: 'zoomPercent' },
    { id: 'zoomOut', type: 'action', action: 'zoomOut', icon: 'minus', title: 'Zoom Out' },
    { id: 'separator', type: 'separator' },
    { id: 'fitView', type: 'action', action: 'fitView', icon: 'fit', title: 'Fit View' },
    { id: 'fullscreen', type: 'action', action: 'fullscreen', icon: 'fullscreen', title: 'Fullscreen' },
  ]
};

// In template:
// <ngx-workflow-diagram [nodes]="nodes" [edges]="edges" [zoomControlsConfig]="zoomConfig" />