Custom commands let you extract repeated logic into a reusable, first-class Cypress command. This lesson covers how to define them, the different command types, and how to add TypeScript types for them.
Cypress.Commands.add()
Cypress.Commands.add() registers a new command on the global cy object, available in every spec file once defined in a support file. It takes a command name, an optional command type, and an implementation function.
// cypress/support/commands.js
Cypress.Commands.add('login', (email, password) => {
cy.visit('/login');
cy.get('[data-cy="email"]').type(email);
cy.get('[data-cy="password"]').type(password);
cy.get('[data-cy="submit"]').click();
});
// in a spec file
cy.login('user@example.com', 'SecurePass123!');
Once defined, cy.login(...) behaves exactly like a built-in command, fully chainable and appearing in the command log.
In a TypeScript project, custom commands need a type declaration merged into Cypress's global Chainable interface, otherwise TypeScript will not recognize them and editor autocompletion will not work.
This declaration file should be referenced from your TypeScript config so the editor and compiler pick it up.
Common Mistakes
Defining a custom command as a child command when it doesn't actually need a previous subject.
Forgetting to add TypeScript type declarations, causing confusing type errors for custom commands.
Putting UI-specific logic inside a command meant to be reused across unrelated features.
Not registering commands in a support file that actually gets loaded before spec files run.
Key Takeaways
Cypress.Commands.add() registers reusable custom commands on the global cy object.
Commands can be parent, child, or dual, depending on whether they need a previous subject.
Custom commands need explicit TypeScript type declarations in TypeScript projects.
Custom commands should live in a support file that loads before every spec.
Pro Tip
Name custom commands as verbs describing intent, like cy.login() or cy.seedCart(), rather than describing implementation, like cy.fillLoginFormAndClickSubmit(), this keeps call sites readable and gives you freedom to change the implementation later without renaming every usage.
You now know how to create custom commands. Next, explore concrete Reusable Commands patterns for common testing needs.