# How to Write Reusable Cypress Code: Custom Commands, Mount Utils, and Shared Fixtures

> Learn to write reusable Cypress code with custom commands, mount utils, and shared fixtures. Boost your testing efficiency and maintainability. Start writing better tests today.

- Repository: [Cypress.io/cypress](https://github.com/cypress-io/cypress)
- Tags: how-to-guide
- Published: 2026-06-18

---

**Cypress supports reusable test code through three core mechanisms: custom commands registered via `Cypress.Commands.add`, framework adapters using `@cypress/mount-utils`, and shared utilities stored in `cypress/support/helpers` and fixtures.**

Writing maintainable end-to-end and component tests requires avoiding duplication across spec files. In the cypress-io/cypress repository, reusability is achieved through a layered architecture that combines custom commands, mount utilities, and shared fixtures to keep test logic DRY while preserving the framework's auto-retry and debugging capabilities.

## Custom Commands for Reusable Test Actions

**Custom commands** are the primary mechanism for encapsulating repetitive actions. According to the cypress-io/cypress source code, you register these in `cypress/support/**/*.js` or `.ts` files using the `Cypress.Commands.add` API. This centralizes logic such as authentication, form filling, or navigation that would otherwise be duplicated across multiple spec files.

### Registering Commands in cypress/support/commands.ts

Place your command definitions in the support directory to ensure they load before test execution. The following example demonstrates a `cy.login` command that combines fixture data with helper functions:

```typescript
// cypress/support/commands.ts
import { login } from '../support/helpers/auth'

Cypress.Commands.add('login', (username: string) => {
  // Pull credentials from a fixture (shared data)
  cy.fixture('users').then((users) => {
    const { password } = users[username]
    login(username, password)   // Calls the reusable helper
  })
})

```

### Chaining Commands with Cypress.Chainable

Commands return `Cypress.Chainable` instances, allowing them to chain with other Cypress commands. This fluent interface ensures that custom commands integrate seamlessly with built-in methods like `cy.visit()` and `cy.get()`. Because the command lives in a single file, any change to the login flow propagates automatically to all specs that call `cy.login()`.

## Component Testing with Mount Utils

For component testing, the `@cypress/mount-utils` package defines a standard contract for framework adapters. The source code in [`npm/mount-utils/README.md`](https://github.com/cypress-io/cypress/blob/main/npm/mount-utils/README.md) specifies that adapters must receive a component, register lifecycle hooks, and call `setupHooks()`. This architecture enables you to write reusable mount commands that work across React, Vue, Svelte, or Angular.

### The Mount Adapter Contract

The framework reserves `cy.mount` for component testing, but you can create framework-specific implementations. The mount-utils library provides the underlying infrastructure that first-party adapters use to ensure consistent behavior. By following this pattern, you create a reusable mount command that handles provider setup, global styles, or initialization logic once.

### Creating Framework-Agnostic Mount Commands

Implement your mount command in [`cypress/support/component.ts`](https://github.com/cypress-io/cypress/blob/main/cypress/support/component.ts) using the mount-utils contract:

```typescript
// cypress/support/component.ts
import { mount } from '@cypress/react'          // first-party React adapter
import { setupHooks } from '@cypress/mount-utils'

Cypress.Commands.add('mount', (Component, props = {}) => {
  // Wrap the component with any required providers here, then call the adapter
  return mount(<Component {...props} />)
})

// Ensure lifecycle hooks are set up once
setupHooks()

```

Use the command in component specs like this:

```typescript
// cypress/component/MyButton.cy.ts
import MyButton from '../../src/MyButton.vue'

describe('MyButton component', () => {
  it('renders correctly', () => {
    cy.mount(MyButton, { props: { label: 'Click me' } })
    cy.get('[data-cy=button]').should('contain', 'Click me')
  })
})

```

## Shared Utilities and Fixtures

The monorepo's `system-tests/project-fixtures` directory demonstrates how to reuse fixture fragments across multiple test projects. These patterns translate directly to application test suites, allowing you to share static data and utility functions.

### Helper Functions in cypress/support/helpers/

Extract pure logic into TypeScript or JavaScript modules to keep commands clean and testable. Store these in `cypress/support/helpers/` and import them where needed:

```typescript
// cypress/support/helpers/auth.ts
export function login(username: string, password: string) {
  cy.visit('/login')
  cy.get('[data-cy=login-username]').type(username)
  cy.get('[data-cy=login-password]').type(password, { log: false })
  cy.contains('button', 'Log in').click()
}

```

### Static Data with cy.fixture

Store shared test data in `cypress/fixtures/` as JSON files. Access these via `cy.fixture()` to ensure consistent test data across all specs. The cypress-io/cypress repository uses this pattern extensively in `system-tests/project-fixtures` for reusable React, Vue, and Angular component fragments.

### Page Object Pattern for Selectors

For complex UIs, implement the **Page Object pattern** to centralize selectors. This creates a reusable layer that insulates tests from DOM changes:

```typescript
// cypress/support/pageObjects/LoginPage.ts
export const loginPage = {
  usernameInput: () => cy.get('[data-cy=login-username]'),
  passwordInput: () => cy.get('[data-cy=login-password]'),
  submitButton: () => cy.contains('button', 'Log in')
}

```

Import and use the page object in your specs:

```typescript
import { loginPage } from '../support/pageObjects/LoginPage'

cy.visit('/login')
loginPage.usernameInput().type('admin')
loginPage.passwordInput().type('secret')
loginPage.submitButton().click()

```

## Summary

- **Custom commands** registered via `Cypress.Commands.add` in [`cypress/support/commands.ts`](https://github.com/cypress-io/cypress/blob/main/cypress/support/commands.ts) provide the primary mechanism for reusable test actions.
- **Mount utilities** from `@cypress/mount-utils` standardize component testing across frameworks through the `setupHooks()` contract and adapter pattern.
- **Shared fixtures** in `cypress/fixtures/` and helper modules in `cypress/support/helpers/` eliminate duplication of test data and logic.
- **Page Object patterns** centralize DOM selectors, making tests resilient to UI changes while maintaining readable spec files.

## Frequently Asked Questions

### What is the difference between a custom command and a helper function in Cypress?

Custom commands extend the `cy` namespace and integrate with Cypress's command log and retry logic, while helper functions are standard JavaScript utilities that encapsulate logic but don't automatically log to the Cypress Command Log or retry. Use `Cypress.Commands.add` for actions that need to chain with other Cypress methods, and regular helper functions for pure data transformation or complex logic that doesn't interact with the DOM.

### Where should I register custom commands in a Cypress project?

Register custom commands in files located in `cypress/support/**/*.js` or `.ts`, typically in a file named [`commands.ts`](https://github.com/cypress-io/cypress/blob/main/commands.ts) or [`commands.js`](https://github.com/cypress-io/cypress/blob/main/commands.js). Cypress automatically loads all files in the `cypress/support` directory before executing spec files, ensuring commands are available globally across your test suite.

### Can I use custom commands with Cypress Component Testing?

Yes, custom commands work identically in component and end-to-end tests. For component testing, the `cy.mount` command is reserved and implemented using `@cypress/mount-utils` adapters. You can add additional custom commands for component-specific actions like `cy.login` or `cy.setupProvider` in your support file alongside the mount configuration.

### How do I share fixtures across multiple test projects?

Store reusable fixture fragments in a shared location such as `system-tests/project-fixtures/` (as implemented in cypress-io/cypress) and copy them into test projects at scaffold time. Alternatively, reference fixtures from a common directory using relative imports or configure Cypress to look for fixtures in multiple paths via the `fixturesFolder` configuration option.