Cypress Commands: Complete Guide to Built-in API and Custom Commands

Cypress commands are asynchronous methods attached to the global cy object that execute in a deterministic command queue, enabling everything from DOM traversal to custom test utilities.

The Cypress commands API forms the backbone of the Cypress testing framework. In the cypress-io/cypress repository, these commands are implemented as a sophisticated queue-based system that handles retries, timeouts, and automatic waiting. Understanding how to leverage both built-in and custom commands is essential for writing reliable end-to-end and component tests.

What Are Cypress Commands?

Cypress commands are the core API methods that drive test execution. They are attached to the global cy object and include methods like cy.visit(), cy.get(), and cy.request(). When a test calls a command, Cypress places it in an internal command queue; the test runner processes this queue step-by-step, handling automatic retries and timeouts.

Commands execute asynchronously but appear synchronous in your code. This deterministic queue ensures that each command waits for the previous one to complete before executing, with built-in retry logic that waits for elements to become actionable.

How Cypress Commands Work

Command Queue and Asynchronous Execution

The command queue is the architectural foundation of Cypress. Located in the driver package, the scheduler manages command chaining, resolves promises, and injects automatic waits between operations. The validation logic for command definitions resides in packages/driver/src/cypress/error_messages.ts, which protects against duplicate or reserved command names.

When you write cy.get('.button').click(), Cypress does not execute these immediately. Instead, it queues both commands, then processes them sequentially with automatic retry-on-failure logic.

Built-in Commands

Built-in commands are implemented in the driver package and expose high-level actions such as DOM traversal, network requests, and filesystem interactions. These commands form the standard API that every Cypress test uses.

// Visit a page and assert the title
cy.visit('/login')
cy.title().should('eq', 'Login – MyApp')

Custom Commands

Users extend the API via Cypress.Commands.add(name, fn) or Cypress.Commands.addQuery(). Custom commands are typically defined in support files (cypress/support/commands.ts or similar), which load before test files execute. This registration process places the new command in the same registry used by core commands, ensuring it participates fully in chaining and retry mechanics.

Using Built-in Cypress Commands

Built-in commands are called directly on the cy object and return chainable objects that allow further commands to be appended.

// Chain built-in commands for comprehensive testing
cy.visit('/dashboard')
cy.get('[data-testid="user-menu"]').click()
cy.url().should('include', '/profile')

Each command receives the subject from the previous command unless explicitly configured otherwise. This subject inheritance enables expressive, readable test flows without manual variable management.

Creating Custom Commands

Simple Custom Commands

Define reusable test utilities by adding commands to the global cy object. The packages/frontend-shared/cypress/support/component.ts file demonstrates this pattern by registering a mount command for component testing.

// packages/frontend-shared/cypress/support/component.ts
Cypress.Commands.add('mount', mount)

Usage in a spec file:

import { MyButton } from './MyButton.vue'

cy.mount(MyButton).get('button').should('contain.text', 'Click me')

Commands with Previous Subject

Use the prevSubject option to create commands that act on an existing subject, such as a DOM element. The packages/launchpad/cypress/e2e/support/dropFileWithPath.ts file illustrates this pattern.

// packages/launchpad/cypress/e2e/support/dropFileWithPath.ts
Cypress.Commands.add(
  'dropFileWithPath',
  { prevSubject: 'element' },
  (subject, filePath) => {
    // Implementation triggers drag-and-drop on the element
    return cy.wrap(subject).trigger('drop', { dataTransfer: new DataTransfer() })
  }
)

Usage in a test:

cy.get('#drop-zone')
  .dropFileWithPath('cypress/fixtures/sample.png')
  .should('have.class', 'has-file')

Setting prevSubject: 'element' ensures the command only executes when chained off an element query, receiving the subject as the first argument.

Custom Queries

Add commands that return computed values rather than chainable objects using Cypress.Commands.addQuery(). These are useful for extracting data from the application under test.

Cypress.Commands.addQuery('getAll', () => {
  return cy.window().then(win => Object.keys(win))
})

Usage:

cy.getAll().should('include', 'myGlobalVar')

Overwriting Existing Commands

Safely modify existing command behavior using Cypress.Commands.overwrite(). This approach is guarded internally to prevent accidental breaking changes.

Cypress.Commands.overwrite('click', (originalFn, element, options) => {
  cy.log('Custom click logging')
  return originalFn(element, options)
})

Key Implementation Files

Understanding where Cypress commands are defined helps when debugging or extending the framework:

Summary

  • Cypress commands are asynchronous methods on the cy object that execute in a deterministic queue with automatic retries and timeouts.
  • Built-in commands cover DOM traversal, network requests, and assertions, implemented in the driver package.
  • Custom commands extend the API via Cypress.Commands.add() and participate in the same retry and chaining logic as core commands.
  • Use prevSubject to create commands that operate on existing DOM elements or other subjects.
  • Cypress.Commands.addQuery() creates commands that return values rather than chainable objects.
  • Cypress.Commands.overwrite() allows safe modification of existing command behavior.

Frequently Asked Questions

What is the difference between Cypress.Commands.add() and Cypress.Commands.addQuery()?

Cypress.Commands.add() creates standard commands that return chainable objects and can be used for actions like clicking or typing. Cypress.Commands.addQuery() creates commands that return a computed value and do not chain further, making them suitable for extracting data from the application state.

How does Cypress handle asynchronous command execution?

Cypress maintains an internal command queue that processes commands sequentially. When you call cy.get(), the command is queued rather than executed immediately. The test runner processes the queue step-by-step, ensuring each command completes before the next begins, with built-in retry logic for handling timing issues.

Can I override built-in Cypress commands?

Yes, use Cypress.Commands.overwrite('commandName', (originalFn, ...args) => { ... }) to modify existing command behavior. This is guarded by validation in packages/driver/src/cypress/error_messages.ts to prevent accidental breaking changes. Always call originalFn() within your overwrite to maintain core functionality.

Where should I define custom commands?

Define custom commands in support files located in cypress/support/, typically commands.ts or e2e.ts. These files load before test files execute, ensuring your custom commands are registered in the global cy object before specs run. This pattern is demonstrated in packages/frontend-shared/cypress/support/component.ts for component testing utilities.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →