Overview

Loops let you repeat a set of steps multiple times. Use them to iterate over UI elements on a page (e.g., all product cards) or over data (e.g., items from an API response) using Start Loop and End Loop. Loops overview showing Start Loop summary and End Loop boundary

At a glance

  • Two sources: Elements on the page or an Expression that evaluates to an array/iterable
  • Loop variable: Name the current item (e.g., item, user, index) and reference it as {{vars.<name>}}
  • Automatic pairing: Adding Start Loop automatically adds End Loop to close the block
  • Presets: Quick-insert expressions like “Loop 5 times” and “API response”

How it works

When you insert a Start Loop, you choose what to iterate over and name the loop variable. All steps added below it run once for each item. End Loop marks where repetition stops.
Start Loop (configure source + loop variable)
├── Step 1 (runs for each item)
├── Step 2 (runs for each item)
└── End Loop

Generated code (conceptual)

// Loop over elements
const items = await page.locator('.product-card').elementHandles();
for (const [index, element] of items.entries()) {
  // Steps execute here
}

// Loop over expression
for (const user of testVars['users']) {
  // Steps execute here
}

Configure in the UI

Iterate over

  • Elements on page: Pick or enter a locator that matches multiple elements. You’ll see an optional English description for readability.
  • Expression (array/iterable): Provide a {{ ... }} expression that evaluates to an array/iterable at runtime, e.g. {{vars.items}}, {{vars.apiResponse.users}}, or {{[...Array(5).keys()]}}.
Start Loop form with Elements vs Expression selector and loop variable

Presets (for Expression)

  • Loop 5 times → inserts {{[...Array(5).keys()]}} and sets loop variable to index
  • API response → inserts {{vars.apiResponse.items}} and sets loop variable to item
Preset chips for Loop 5 times and API response

Loop variable name

  • Defaults to item. Must be a valid identifier.
  • Use it inside steps as {{vars.item}}, or whatever name you chose.

Expression tips

  • Use {{ ... }} syntax to supply arrays/iterables, e.g., {{vars.users}}, {{[...Array(5).keys()]}}
  • Normalize data before looping if needed: {{vars.users.filter(u => u.active)}}
  • Keep items small/light; avoid passing huge payloads; prefer extracting needed fields first

Examples

Iterate over elements

  • Iterate over: Elements on page
  • Element locator: .product-card
  • Loop variable: card
Inside the loop:
  1. Click “Add to cart” within the current card
  2. Verify cart count increases

Iterate over data

  • Iterate over: Expression (array/iterable)
  • Expression: {{vars.users}}
  • Loop variable: user
Expression loop with preview of users array Inside the loop:
  1. Fill #name with {{vars.user.name}}
  2. Fill #email with {{vars.user.email}}
  3. Submit form

Fixed count

  • Iterate over: Expression (array/iterable)
  • Expression: {{[...Array(5).keys()]}}
  • Loop variable: index
Inside the loop:
  1. Click retry
  2. Use a conditional to stop when successful

Best practices

  • Prefer stable locators: Use data-testid for element loops to avoid flakiness
  • Keep loops small: Limit steps inside the loop; extract long sequences into Snippets
  • Name variables clearly: user, row, card are better than item1
  • Be mindful of size: Very large loops increase runtime; sample or narrow selectors
  • Compose with conditions: Combine with Start/End Condition for branching or early exits

Troubleshooting

  • Selector matches 0 items: Verify the locator; add waits if content is async
  • Expression not iterable: Ensure it evaluates to an array or iterable (e.g., [], result of map/filter)
  • Index confusion: [...Array(5).keys()] yields 0–4

Performance considerations

  • Prefer stable selectors; resolve element lists once, then reuse inside the loop
  • Avoid large loops when possible; sample or cap the iteration count
  • Move expensive setup outside the loop; keep loop body minimal