---
id: api/commands/each
title: cy.each()
description: >-
  Iterate through an array like structure in Cypress (arrays or objects with a
  length property).
section: api
source_path: docs/api/commands/each.mdx
version: f60251bf707b0077a9535579c9f90d615440cc35
updated_at: '2026-10-16T10:56:58.670Z'
---
# each

Iterate through an array like structure (arrays or objects with a `length` property).

## Syntax

```
.each(callbackFn)
.each(options, callbackFn)
```

### Usage

**Correct Usage**

```
cy.get('ul>li').each(() => {...}) // Iterate through each 'li'
cy.getCookies().each(() => {...}) // Iterate through each cookie
```

**Incorrect Usage**

```
cy.each(() => {...})            // Errors, cannot be chained off 'cy'
cy.clock().each(() => {...})    // Errors, 'clock' does not yield an array
```

### Arguments

**options _(Object)_**

Pass in an options object to change the default behavior of `.each()`.

| Option | Default | Description |
| --- | --- | --- |
| `timeout` | [`defaultCommandTimeout`](/llm/markdown/app/references/configuration.md#Timeouts) | Time to wait for each invocation of the callback function to resolve before [timing out](#Timeouts) |

**callbackFn _(Function)_**

Pass a function that is invoked with the following arguments:

*   `value` - the current item in the collection. If the item is a DOM element, Cypress wraps it in jQuery before passing it to the callback.
*   `index` - the 0-based index of the current item in the collection
*   `collection` - the original collection being iterated over

### Yields

*   `.each()` yields the same subject it was given.
*   Because `.each()` is not a [query](/llm/markdown/app/core-concepts/retry-ability.md#Only-queries-are-retried), it is unsafe to chain further commands that act or assert on DOM elements after `.each()`, as those elements may have re-rendered and gone stale during iteration. Reading the yielded subject in a [`.then()`](/llm/markdown/api/commands/then.md) callback (as in the example below) is safe.

## Examples

### DOM Elements

**Iterate over an array of DOM elements**

Each element arrives in the callback wrapped in jQuery, so you can read its state with jQuery methods. Wrap it with [`cy.wrap()`](/llm/markdown/api/commands/wrap.md) to run Cypress commands on it.

```
cy.get('[data-cy="product-card"]').each(($card) => {
  if ($card.find('[data-cy="sold-out"]').length) {
    cy.wrap($card).find('button').should('be.disabled')
  } else {
    cy.wrap($card).find('button').should('be.enabled')
  }
})
```

**Assert the expected length before iterating**

[`cy.get()`](/llm/markdown/api/commands/get.md) resolves as soon as it finds at least one matching element, and `.each()` does not retry. If your application renders a list in stages, `.each()` iterates only the items that exist at that moment. Assert on the expected length first so the test waits for every item.

```
cy.get('[data-cy="result"]')
  .should('have.length', 2)
  .each(($result) => {
    cy.wrap($result).should('contain', 'Cypress')
  })
```

**Scope commands to each element with `.within()`**

```
cy.get('table tbody tr').each(($row) => {
  cy.wrap($row).within(() => {
    cy.get('td').eq(2).should('not.be.empty')
  })
})
```

**Re-query by index when an action changes the page**

If an action in the callback navigates away or re-renders the list, the elements `.each()` passes in are detached from the DOM. Use `index` to query a fresh element on every iteration instead.

```
cy.get('nav a').each(($link, index) => {
  cy.get('nav a').eq(index).click()
  cy.go('back')
})
```

**Collect results from Cypress commands, then assert once**

When each item needs a Cypress command, such as a request, gather the results in the callback and assert on all of them in a [`.then()`](/llm/markdown/api/commands/then.md) that runs after the loop finishes. This test reports every broken link at once instead of stopping at the first one.

```
const broken = []

cy.get('footer a')
  .each(($link) => {
    const href = $link.prop('href')

    cy.request({ url: href, failOnStatusCode: false }).then((response) => {
      if (response.status >= 400) {
        broken.push(`${href} (${response.status})`)
      }
    })
  })
  .then(() => {
    // list the links in the message, since Chai shortens long arrays
    expect(broken, `broken links: ${broken.join(', ')}`).to.be.empty
  })
```

If you only need to read values from the elements, such as their text, use a [`.should()` callback](#Prefer-should-for-assertions-on-every-element) instead.

**The original collection is always yielded**

No matter what the callback function returns, `.each()` always yields the original collection.

```
cy.get('li')
  .should('have.length', 3)
  .each(($li, index, $lis) => {
    return 'something else'
  })
  .then(($lis) => {
    expect($lis).to.have.length(3) // true
  })
```

Here the `.then()` callback only _reads_ the yielded array, which is safe. If instead you needed to act or assert on DOM elements that your application may have re-rendered during iteration, re-query them with a Cypress query rather than reusing the subject yielded by `.each()`.

### Arrays

**Iterate over fixture data**

`.each()` works on any array, not only DOM elements. Cypress commands you call in the callback run in order, one item at a time.

```
// users.json contains an array of user objects
cy.fixture('users.json').each((user) => {
  cy.request('POST', '/api/users', user).its('status').should('eq', 201)
})
```

### Promises

**Promises are awaited**

If your callback function returns a `Promise`, `.each()` waits for it to resolve before iterating over the next item in the collection.

```
cy.wrap([1, 2, 3]).each((num, i, array) => {
  return new Cypress.Promise((resolve) => {
    setTimeout(() => {
      resolve()
    }, num * 100)
  })
})
```

### Options

**Wait longer for each callback to resolve**

The `timeout` option applies to each invocation of the callback, not to the loop as a whole.

```
cy.getCookies().each({ timeout: 10000 }, (cookie) => {
  return new Cypress.Promise((resolve) => {
    // work with each cookie, then resolve
    resolve()
  })
})
```

## Notes

### Prefer `.should()` for assertions on every element

**Use a `.should()` callback when you only need to assert**

`.each()` runs its callback once and does not [retry](/llm/markdown/app/core-concepts/retry-ability.md). When all you need is an assertion about every element, a [`.should()` callback](/llm/markdown/api/commands/should.md#Function) reruns until the assertions pass or time out, so it tolerates elements that are still rendering or updating.

```
cy.get('[data-cy="result"]').should(($results) => {
  expect($results).to.have.length.greaterThan(0)

  $results.each((index, result) => {
    expect(result.textContent).to.contain('Cypress')
  })
})
```

Because the whole callback reruns, the same pattern works for checks on the list as a whole, such as sort order after your application applies a sort.

```
cy.get('[data-cy="price"]').should(($prices) => {
  const prices = [...$prices].map((price) =>
    Number(price.textContent.replace('$', ''))
  )

  expect(prices).to.deep.equal([...prices].sort((a, b) => a - b))
})
```

Chaining `.should()` after `.each()` does not give you this retry. `.should()` reruns only its own callback, not `.each()`, so it keeps checking values collected from the list as it was when `.each()` ran.

Keep `.each()` for work that needs Cypress commands per item, such as clicking, typing, or making requests.

### Return early

**Stop `each` prematurely**

You can stop the `.each()` loop early by returning `false` in the callback function. The callback must return `false` synchronously. A `Promise` that resolves to `false` does not stop the loop.

```
cy.get('ul>li').each(($li) => {
  if ($li.hasClass('last-visible')) {
    // stop iterating after this item
    return false
  }
})
```

### Empty collections

**The callback is not invoked for an empty collection**

When the subject has a `length` of `0`, `.each()` skips the callback entirely and yields the empty subject. It does not fail or wait for items to appear.

A DOM query such as [`cy.get()`](/llm/markdown/api/commands/get.md) fails when no elements match, so an empty subject usually comes from non-DOM data, such as `cy.wrap([])`, [`cy.fixture()`](/llm/markdown/api/commands/fixture.md), or [`cy.getCookies()`](/llm/markdown/api/commands/getcookies.md). Assert on the length first when the data should never be empty.

### Callback return values

**Don't mix Cypress commands with a synchronous return value**

The callback follows the same rules as a [`.then()`](/llm/markdown/api/commands/then.md) callback. If it calls Cypress commands **and** returns a truthy synchronous value (anything other than a falsy value or a `Promise`\-like object with a `.then()` method), Cypress throws an error because you are mixing up async and sync code. Whatever the callback returns, `.each()` still yields the original collection.

## Rules

### Requirements

*   `.each()` requires being chained off a previous command.
*   `.each()` requires its subject to have a `length` property, such as an array or a jQuery collection. Any other subject fails with an error.

### Assertions

*   `.each()` will only run assertions you have chained once, and will not [retry](/llm/markdown/app/core-concepts/retry-ability.md).

### Timeouts

*   `.each()` can time out waiting for a promise you've returned to resolve. The timeout applies to each invocation of the callback and defaults to [`defaultCommandTimeout`](/llm/markdown/app/references/configuration.md#Timeouts).

## Command Log

*   `.each()` does _not_ log in the Command Log unless it fails, for example when its callback times out or its subject is not array-like.

## See also

*   [`.spread()`](/llm/markdown/api/commands/spread.md)
*   [`.then()`](/llm/markdown/api/commands/then.md)
