Skip to main content

Overview

The Loop node enables iteration in workflows. It supports two modes: iterating over arrays (forEach) and repeating while conditions are true (doWhile). Loop nodes are containers that execute child nodes repeatedly.

Loop Modes

forEach

Iterate over an array, executing child nodes for each item.
mode
string
required
Set to forEach for array iteration.
arrayVariable
string
required
Context variable name containing the array to iterate over.
Loop Variables:
  • ${data.item} - Current array item
  • ${data.index} - Current iteration index (0-based)
Example:
{
  "type": "loop",
  "data": {
    "mode": "forEach",
    "arrayVariable": "products"
  }
}
// Inside loop:
// ${data.item.name} - Product name
// ${data.item.price} - Product price
// ${data.index} - Current index

doWhile

Repeat child nodes while a condition is true.
mode
string
required
Set to doWhile for conditional looping.
condition
object
required
Condition to evaluate after each iteration.Condition types:
  • selector - Check if element exists
  • url - Check if URL matches
  • javascript - Custom JavaScript condition
maxIterations
number
default:"1000"
Maximum iterations to prevent infinite loops.
updateStep
string
JavaScript code to execute after each iteration (e.g., increment counter).
Loop Variables:
  • ${data.index} - Current iteration index (0-based)
  • ${data.item} - Set to null (not used in doWhile)

Configuration

mode
string
required
Loop mode: forEach or doWhile
failSilently
boolean
default:"false"
If true, errors in loop setup log warnings but don’t stop execution.

Examples

forEach Loops

// First, extract products
{
  "type": "dataExtractor",
  "data": {
    "containerSelector": ".product-card",
    "fields": [
      { "name": "title", "selector": ".title", "extract": "text" },
      { "name": "price", "selector": ".price", "extract": "text" }
    ],
    "outputVariable": "products"
  }
}

// Then, loop over products
{
  "type": "loop",
  "data": {
    "mode": "forEach",
    "arrayVariable": "products"
  },
  "children": [
    {
      "type": "javascriptCode",
      "data": {
        "code": "console.log(`Product ${context.getData('index')}: ${context.getData('item').title} - ${context.getData('item').price}`)"
      }
    }
  ]
}

doWhile Loops

{
  "type": "loop",
  "data": {
    "mode": "doWhile",
    "condition": {
      "type": "selector",
      "value": "button.next-page",
      "visibility": "visible"
    },
    "maxIterations": 10
  },
  "children": [
    {
      "type": "action",
      "data": {
        "action": "click",
        "selector": "button.next-page"
      }
    },
    {
      "type": "wait",
      "data": {
        "waitType": "timeout",
        "value": 2000
      }
    }
  ]
}

Complex Iterations

// Outer loop: categories
{
  "type": "loop",
  "data": {
    "mode": "forEach",
    "arrayVariable": "categories"
  },
  "children": [
    {
      "type": "navigation",
      "data": {
        "action": "navigate",
        "url": "https://example.com/${data.item.slug}"
      }
    },
    {
      "type": "dataExtractor",
      "data": {
        "containerSelector": ".product",
        "fields": [...],
        "outputVariable": "categoryProducts"
      }
    },
    // Inner loop: products in category
    {
      "type": "loop",
      "data": {
        "mode": "forEach",
        "arrayVariable": "categoryProducts"
      },
      "children": [
        {
          "type": "javascriptCode",
          "data": {
            "code": "console.log(context.getData('item').name)"
          }
        }
      ]
    }
  ]
}

Loop Variables Access

const currentIndex = context.getData('index');
const currentItem = context.getData('item');

console.log(`Processing item ${currentIndex}`);
console.log(`Item data:`, currentItem);

// Modify item data
context.setData('processedItem', {
  ...currentItem,
  processed: true
});

Loop Control Patterns

Early Exit

// Inside loop JavaScript Code node
const item = context.getData('item');
if (item.shouldStop) {
  // Set flag to exit
  context.setData('breakLoop', true);
}

Skip Items

// Conditional processing
const item = context.getData('item');
if (item.skip) {
  return; // Skip to next iteration
}
// Continue processing

Accumulate Results

// Collect results from iterations
const results = context.getData('loopResults') || [];
const currentResult = context.getData('currentResult');
results.push(currentResult);
context.setData('loopResults', results);

Common Use Cases

Pagination

[
  // Navigate to first page
  {
    "type": "navigation",
    "data": { "action": "navigate", "url": "https://example.com/page/1" }
  },
  // Loop through pages
  {
    "type": "loop",
    "data": {
      "mode": "doWhile",
      "condition": {
        "type": "selector",
        "value": ".next-page",
        "visibility": "visible"
      },
      "maxIterations": 50
    },
    "children": [
      { "type": "dataExtractor", "data": { ... } },
      { "type": "action", "data": { "action": "click", "selector": ".next-page" } },
      { "type": "wait", "data": { "waitType": "timeout", "value": 1000 } }
    ]
  }
]

Batch Processing

{
  "type": "loop",
  "data": {
    "mode": "forEach",
    "arrayVariable": "filePaths"
  },
  "children": [
    {
      "type": "apiRequest",
      "data": {
        "url": "https://api.example.com/upload",
        "method": "POST",
        "formFiles": [{ "name": "file", "filePath": "${data.item}" }]
      }
    }
  ]
}

Notes

The maxIterations parameter in doWhile loops prevents infinite loops by limiting the maximum number of iterations.
Loop variables (item and index) are overwritten in nested loops. Store values in separate context variables if needed for outer loop access.
The doWhile loop always executes at least once before checking the condition.

Build docs developers (and LLMs) love