---
id: api/commands/then
title: cy.then()
description: >-
  Enables you to work with the subject yielded from the previous command in
  Cypress.
section: api
source_path: docs/api/commands/then.mdx
version: 3163d68b20e695f2c76d40c85c3f3b956dd19a3b
updated_at: '2026-08-21T20:59:04.402Z'
---
# then

Enables you to work with the subject yielded from the previous command.

**Note:** `.then()` assumes you are already familiar with core concepts such as [closures](/llm/markdown/app/core-concepts/variables-and-aliases.md#Closures).

**Note:** Prefer [`.should()` with callback](/llm/markdown/api/commands/should.md#Function) over `.then()` for assertions as they are automatically rerun until no assertions throw within it but be aware of [differences](/llm/markdown/api/commands/should.md#Differences).

## Syntax

```
.then(callbackFn)
.then(options, callbackFn)
```

### Usage

**Correct Usage**

```
cy.get('.nav').then(($nav) => {}) // Yields .nav as first arg
cy.location().then((loc) => {}) // Yields location object as first arg
```

### Arguments

**options _(Object)_**

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

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

**callbackFn _(Function)_**

Pass a function that takes the previously yielded subject as its first argument.

### Yields

Whatever is returned from the callback function becomes the new subject and will flow into the next command (with the exception of `undefined` or `null`).

*   If the return value is a chain of Cypress commands (eg `return cy.get('button')`), Cypress will wait for them to resolve and use their return value as the new subject.
*   If the return value is a Promise, Cypress will wait for it to resolve, and use the resolved value as the new subject to continue the chain of commands.
*   If the callback returns `undefined` or `null` (or there is no return value), the result of the last Cypress command in the callback function will be yielded as the new subject instead, and flow into the next command.
*   If the callback returns `undefined` or `null` (or there is no return value) and the callback does not call any Cypress commands, the subject will not be modified, and the previous subject will carry over to the next command.
*   If the callback calls Cypress commands **and** returns a non-`undefined`, non-`null` synchronous value, Cypress will throw an error: _"cy.then() failed because you are mixing up async and sync code."_ To return a new synchronous value while also using Cypress commands, wrap the return value in a nested `.then()` that contains no Cypress commands (see example below).

The callback function of `.then()` is not retried. It is [unsafe](/llm/markdown/app/core-concepts/retry-ability.md#Only-queries-are-retried) to return DOM elements directly from the callback and then use further commands on them. Instead, use Cypress queries to locate the elements you're interested in acting or asserting on.

## Examples

We have several more examples in our [Core Concepts Guide](/llm/markdown/app/core-concepts/variables-and-aliases.md) which go into the various ways you can use `.then()` to store, compare, and debug values.

### DOM element

#### The `button` element is yielded

```
cy.get('button').then(($btn) => {
  const cls = $btn.attr('class')

  cy.wrap($btn).click().should('not.have.class', cls)
})
```

#### The number is yielded from previous command

```
cy.wrap(1)
  .then((num) => {
    cy.wrap(num).should('equal', 1) // true
  })
  .should('equal', 1) // true
```

### Change subject

#### The el subject is changed with another command

```
cy.get('button')
  .then(($btn) => {
    const cls = $btn.attr('class')

    cy.wrap($btn).click().should('not.have.class', cls).find('i')
    // since there is no explicit return
    // the last Cypress command's yield is yielded
  })
  .should('have.class', 'spin') // assert on i element
```

#### The number subject is changed with another command

```
cy.wrap(1)
  .then((num) => {
    cy.wrap(num).should('equal', 1) // true
    cy.wrap(2)
  })
  .should('equal', 2) // true
```

#### The number subject is changed by returning

```
cy.wrap(1)
  .then((num) => {
    cy.wrap(num)
      .should('equal', 1) // true
      .then(() => {
        return 2
      })
  })
  .should('equal', 2) // true
```

#### Returning `undefined` will not modify the yielded subject

```
cy.get('form')
  .then(($form) => {
    console.log('form is:', $form)
    // undefined is returned here, but $form will be
    // yielded to allow for continued chaining
  })
  .find('input')
  .then(($input) => {
    // we have our $input element here since
    // our form element was yielded and we called
    // .find('input') on it
  })
```

### Raw HTMLElements are wrapped with jQuery

```
cy.get('div')
  .then(($div) => {
    return $div[0] // type => HTMLDivElement
  })
  .then(($div) => {
    $div // type => JQuery<HTMLDivElement>
  })
```

### Promises

Cypress waits for Promises to resolve before continuing

#### Example using Q

```
cy.get('button')
  .click()
  .then(($button) => {
    const p = Q.defer()

    setTimeout(() => {
      p.resolve()
    }, 1000)

    return p.promise
  })
```

#### Example using bluebird

```
cy.get('button')
  .click()
  .then(($button) => {
    return Promise.delay(1000)
  })
```

#### Example using jQuery deferred's

```
cy.get('button')
  .click()
  .then(($button) => {
    const df = $.Deferred()

    setTimeout(() => {
      df.resolve()
    }, 1000)

    return df
  })
```

## Notes

### Differences

### What's the difference between `.then()` and `.should()`/`.and()`?

Using `.then()` allows you to use the yielded subject in a callback function and should be used when you need to manipulate some values or do some actions.

When using a callback function with `.should()` or `.and()`, on the other hand, there is special logic to rerun the callback function until no assertions throw within it. You should be careful of side affects in a `.should()` or `.and()` callback function that you would not want performed multiple times.

### Multiple actions on the same element

A common pattern is to perform several sequential actions on the same element — for example, focusing an input, clearing its value, typing a new value, and then blurring it. Issue each action as a separate command — there is no need to wrap them in `.then()`:

```
// ✅ Preferred: separate commands
cy.get('input').focus()
cy.get('input').clear()
cy.get('input').type('new value')
cy.get('input').blur()
```

You might be tempted to wrap multiple actions inside `.then()` to avoid re-querying the element:

```
// ⚠️ Unnecessary: .then() is not retried and does not improve retry-ability
cy.get('input').then(($input) => {
  cy.wrap($input).focus()
  cy.wrap($input).clear()
  cy.wrap($input).type('new value')
  cy.wrap($input).blur()
})
```

This pattern is unnecessary and can be misleading. The `.then()` callback is **not** retried — so it does not protect against detached DOM errors any better than direct chaining. In addition, `$input` inside the callback is a snapshot of the element at the time `.then()` ran; if the DOM updates and the element is replaced, the wrapped reference becomes stale.

For sequential actions that need to be split across separate chains (for example, when each action might trigger a re-render), use separate `cy.get()` calls or an [alias](/llm/markdown/app/core-concepts/variables-and-aliases.md):

```
// ✅ Separate queries re-run before each action, protecting against re-renders
cy.get('input').focus()
cy.get('input').clear()
cy.get('input').type('new value')
cy.get('input').blur()

// ✅ Equivalent with an alias
cy.get('input').as('field')
cy.get('@field').focus()
cy.get('@field').clear()
cy.get('@field').type('new value')
cy.get('@field').blur()
```

See the [Retry-ability guide](/llm/markdown/app/core-concepts/retry-ability.md) for a deeper explanation of how Cypress retries queries before each action.

## Rules

### Requirements

*   `.then()` requires being chained off a previous command.

### Assertions

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

### Timeouts

*   `.then()` can time out waiting for a promise you've returned to resolve.

## Command Log

*   `.then()` does _not_ log in the Command Log

## History

| Version | Changes |
| --- | --- |
| [0.14.0](/llm/markdown/app/references/changelog.md#0-14-0) | Added `timeout` option |
| [< 0.3.3](/llm/markdown/app/references/changelog.md#0-3-3) | `.then()` command added |

## See also

*   [`.and()`](/llm/markdown/api/commands/and.md)
*   [`.each()`](/llm/markdown/api/commands/each.md)
*   [`.invoke()`](/llm/markdown/api/commands/invoke.md)
*   [`.its()`](/llm/markdown/api/commands/its.md)
*   [`.should()`](/llm/markdown/api/commands/should.md)
*   [`.spread()`](/llm/markdown/api/commands/spread.md)
*   [Guide: Using Closures to compare values](/llm/markdown/app/core-concepts/variables-and-aliases.md#Closures)
*   [Guide: Chains of Commands](/llm/markdown/app/core-concepts/introduction-to-cypress.md#Chains-of-Commands)
*   [Guide: Retry-ability](/llm/markdown/app/core-concepts/retry-ability.md)
*   [Guide: Only queries are retried](/llm/markdown/app/core-concepts/retry-ability.md#Only-queries-are-retried)
