Cypress: Getting Started

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

Cypress is an end-to-end and component test runner for web applications that executes your tests inside the browser, next to the application, and ships its own interactive app for writing and debugging them. This page takes you from an empty project to a running suite and then through every day-to-day command, option and setting you need first. It was written against Cypress 16.2.0 (released 13 October 2026) and checked on 25 October 2026. Every example that tests an application was run against the Bookshelf application, started on a local port, with Cypress 16.2.0 and Chrome 155 on macOS. The interactive parts of the product (the Launchpad and cypress open) need a display and a person to click, so they are described from the official documentation and were not run. For why Cypress is built the way it is, see How Browser Automation Works; for choosing it over Playwright or Selenium, see Choosing a Tool. The commands, ideas and files here come back in the rest of the Cypress pages, starting with Core Concepts.

System requirements

Cypress 16 documents these requirements:

Item Requirement

Node.js

22.x, 24.x or 26.x and later. Node.js 20 and 25 were dropped in 16.0.0. Cypress itself bundles Node.js 24.15.0 for the code it runs in setupNodeEvents and cy.task(), whatever Node.js you installed.

macOS

13.5 or later, on Intel or Apple Silicon.

Linux

x64 or arm64: Ubuntu 22.04 or later (arm64 needs 24.04 or later), Debian 12 or later (arm64 needs 13 or later), Fedora 43 or later. The prebuilt binary needs a glibc-based distribution (glibc 2.31 or later since Cypress 15).

Windows

Windows 10 and 11 (x64); Windows 11 25H2 on arm64 runs in x64 emulation and is a preview; Windows Server 2019, 2022 and 2025.

Package manager

npm 10.1.0 or later, Yarn Classic 1.22.22 or later, Yarn 4 or later, pnpm 8 or later, or Bun 1.2.22 or later.

Hardware

Any modern development machine. In CI use at least 2 CPUs and 4 GB of RAM, and 8 GB or more for long runs or when recording video. Abrupt exits, frozen video frames and slow runs are the usual signs of too little.

Supported browsers

Cypress supports the latest three major versions of Google Chrome, Microsoft Edge and Mozilla Firefox (Firefox 140 or later, because Cypress automates Firefox through WebDriver BiDi only). It also finds Chrome for Testing, Chromium, the beta, canary and developer channels of the Chrome and Edge families, Firefox Developer Edition and Nightly, and offers experimental WebKit support. Every release also bundles an Electron-based Chromium browser. Electron has been deprecated as a test browser since Cypress 16.0.0 and will be removed in a future major version, so the examples on this page always name an installed browser. The reasons the project gives are that Electron’s Chromium trails the current Chrome by weeks or months and that its behaviour diverges from the browser your users run.

When you pass no --browser flag and no defaultBrowser is set, Cypress still falls back to Electron, and prints a warning. This is the real output of cypress run with no browser chosen:

  │ Browser:        Electron 146 (headless) (deprecated)                                           │

Warning: The Electron browser is deprecated as a test browser and will be removed in a future version of Cypress.

Switch to Chrome or another installed browser to avoid a breaking change when you upgrade.

Read more about supported browsers: https://on.cypress.io/launching-browsers

Linux libraries and Docker

On a bare Linux machine Cypress needs a few system libraries. For Ubuntu 22.04 and Debian the documented command is apt-get install libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6 xauth xvfb; on Ubuntu 24.04 and later the libgtk-3-0t64 and libasound2t64 package names replace two of them. The Cypress Docker images (cypress/base, cypress/browsers and cypress/included) already contain everything, and the cypress/factory image builds a custom combination of Node.js, browser and Cypress versions. Install unzip as well, so that the binary download does not fall back to a slower unzip implemented in Node.js. Running in CI is covered in the continuous-integration pages of this section.

Installing Cypress

Install Cypress as a development dependency of the project that holds your tests. The cypress npm package is a small command line wrapper; its postinstall script downloads the real application, called the binary, into a per-user cache that every project on the machine shares.

mkdir bookshelf-e2e && cd bookshelf-e2e
npm init -y
npm install cypress --save-dev
npm install typescript @types/node --save-dev   # needed for TypeScript specs, see below

The first command printed (from an empty project, with the binary already in the cache, so no download happened):

added 171 packages, and audited 172 packages in 2s

found 0 vulnerabilities
npm warn install-scripts 1 package has install scripts not yet covered by allowScripts:
npm warn install-scripts   cypress@16.2.0 (postinstall: node dist/index.js --exec install)
npm warn install-scripts
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow.

That last warning matters. Package managers are moving to block dependency lifecycle scripts by default, and Cypress depends on one to fetch its binary: npm 11.16.0 and later warn (as above) and npm 12.0.0 will block, Yarn 4.14.0 and later block, pnpm requires an allow-list entry, and Bun runs the script only for trusted dependencies. When the script is blocked the binary is not downloaded and Cypress will not start. The fix differs per package manager:

Package manager Allow the Cypress install script

npm 11.16 or later

npm install-scripts approve cypress --no-allow-scripts-pin followed by npm rebuild cypress, or add "allowScripts": { "cypress": true } to package.json.

Yarn 4.14 or later

In .yarnrc.yml: enableScripts: true and a npmPreapprovedPackages list that contains cypress. Use nodeLinker: node-modules, because component testing does not work with Plug’n’Play.

pnpm

pnpm --allow-build=cypress add --save-dev cypress, and turn the side-effects cache off with pnpm config set side-effects-cache false --location project.

Bun

bun pm trust cypress, or add "trustedDependencies": ["cypress"] to package.json; or run bun install --ignore-scripts followed by bunx cypress install.

Any

Skip the script and install the binary explicitly: npx cypress install.

Checking the installation

Four commands tell you what is installed. All of them were run:

npx cypress verify
# ❯  Verifying Cypress can run ~/Library/Caches/Cypress/16.2.0/Cypress.app
# ✔  Verified Cypress!       ~/Library/Caches/Cypress/16.2.0/Cypress.app

npx cypress version
# Cypress package version: 16.2.0
# Cypress binary version: 16.2.0
# Electron version: 41.7.0
# Bundled Node version: 24.15.0

npx cypress cache path
# ~/Library/Caches/Cypress

npx cypress info
# Detected 2 browsers installed:
#
# 1. Chrome
#   - Name: chrome
#   - Channel: stable
#   - Version: 155.0.8059.85
#   - Executable: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
# 2. Firefox
#   - Name: firefox
#   - Channel: stable
#   - Version: 153.0.4
# ...
# Binary Caches: ~/Library/Caches/Cypress
# Cypress Version: 16.2.0 (stable)

The related commands are cypress install --force (download the binary again even if the cache holds it), cypress cache list (with --size), cypress cache prune (keep only the version the project uses) and cypress cache clear. Useful environment variables are CYPRESS_INSTALL_BINARY=0 (install the package without the binary), CYPRESS_CACHE_FOLDER (move the cache), CYPRESS_VERIFY_TIMEOUT (default 30 seconds) and CYPRESS_SKIP_VERIFY=true. Since 16.0.0, cypress info no longer prints proxy variables or CYPRESS_* variables, so its output is safe to paste into a bug report.

Opening the app: the Launchpad

Cypress has two ways to run tests, which are covered in detail under cypress open and cypress run. The first command you type in a new project is the interactive one:

npx cypress open

The Cypress Launchpad appears. It is the setup wizard for a project and it also decides which browser to start. The first time it runs in a project it asks three things, in this order:

flowchart LR A[npx cypress open] --> B[1. Choose a testing type] B --> C[2. Review generated configuration] C --> D[3. Pick a browser and click Start] D --> E[Specs page in the Cypress app]
  1. Choose a testing type. E2E Testing runs your whole application in the browser and visits pages the way a user would. Component Testing mounts single components in isolation. The choice is not permanent; the other type can be set up later.

  2. Review the generated configuration. The Launchpad lists the files it will create for the chosen type and writes them when you click Continue: cypress.config.ts (or .js for a JavaScript project), the support files, and an example fixture. If you chose Component Testing there are two extra screens first, which detect your front-end framework and bundler and check that the dependencies are installed.

  3. Launch a browser. The Launchpad shows the compatible browsers it found on the machine, including Electron. You can switch browser at any time, even in the middle of a session.

Once the project is configured you can skip the wizard by choosing the testing type and the browser on the command line. The Launchpad still opens briefly, because it handles configuration errors, and then lands directly on the Specs page:

npx cypress open --e2e --browser chrome

The Launchpad is the only thing that writes the initial configuration. cypress run in a folder without a configuration file does not scaffold one; it stops. This is the real output:

$ npx cypress run
Could not find a Cypress configuration file in this folder: /path/to/empty-project

If you prefer to create the files by hand, the section The Cypress configuration file and the folder layout under Folder structure are all you need; the Launchpad adds nothing else.

You can use the Cypress app without an account. Signing in to Cypress Cloud from the profile menu adds run history, a Debug page for CI failures and the AI features of Studio and cy.prompt; those are covered in Cypress Cloud.

Choosing a testing type

Cypress names four kinds of test and the Launchpad offers the first two:

Type What it does Reach for it when

End-to-end (E2E)

Visits URLs in a real browser and exercises the whole application, front end to back end and third-party services, the way a user does.

Authentication and purchase flows, data that has to survive several screens, smoke tests before a deployment. Slower and harder to set up, because it needs a running back end.

Component

Mounts one component with cy.mount() and tests it on its own, in the same real browser. The test lives next to the component.

Many states and props of a button, a form or a table. Fast and independent of any server, but passing component tests say nothing about the application as a whole.

API

Sends requests with cy.request() and asserts on the responses, with no UI.

The REST contract, and fast set-up of state for UI tests.

Accessibility

Checks pages or components against accessibility rules, with an axe-based plugin or the Cypress Accessibility product.

Catching regressions in labels, roles and contrast early.

A healthy suite combines them, the way the test pyramid recommends. This page and the next ones concentrate on end-to-end tests; component testing has its own page, Component Testing.

Your first end-to-end test

The official tutorial writes a spec against Cypress’s own demo site. The same steps against Bookshelf are shown here, so that you can compare them with the Playwright page in this section. Start Bookshelf in another terminal (the application is described in Example Application & Test Layout):

cd bookshelf
PORT=3921 BOOKSHELF_REVIEW_DELAY_MS=100 node server.js

Every Cypress test follows three phases: put the application into a known state, take an action, and assert on the result. The recommended shape of a chain is query, query, then an action or assertions. In the Cypress vocabulary that becomes four steps: visit a page, query for an element, interact with it, and assert on the outcome. The spec below contains each step as its own test. It uses cy.task('resetBookshelf') (defined in The Cypress configuration file) to restore the 24 seeded books before each test, and a custom command getByTestId (defined under TypeScript):

// cypress/e2e/first-test.cy.ts
describe('My first Bookshelf test', () => {
  beforeEach(() => {
    cy.task('resetBookshelf');
  });

  it('does not do much', () => {
    expect(true).to.equal(true);
  });

  it('visits the catalogue', () => {
    cy.visit('/books');
    cy.title().should('include', 'Bookshelf');
  });

  it('finds a book, opens it and checks the address', () => {
    cy.visit('/');
    cy.contains('A Field Guide to Clouds').click();
    cy.url().should('include', '/books/3');
    cy.getByTestId('book-detail').should('contain', 'Nora Quill');
  });

  it('types into the search box and filters the catalogue', () => {
    cy.visit('/books');
    cy.getByTestId('search-input').type('quiet{enter}');
    cy.getByTestId('book-card').should('have.length', 1);
    cy.getByTestId('search-input').should('have.value', 'quiet');
  });
});

The run (headless Chrome, BASE_URL pointing at the local server):

BASE_URL=http://localhost:3921 npx cypress run --browser chrome --spec cypress/e2e/first-test.cy.ts
  │ Cypress:        16.2.0                                                                         │
  │ Browser:        Chrome 155 (headless)                                                          │
  │ Specs:          1 found (first-test.cy.ts)                                                     │

  My first Bookshelf test
    ✓ does not do much (46ms)
    ✓ visits the catalogue (88ms)
    ✓ finds a book, opens it and checks the address (126ms)
    ✓ types into the search box and filters the catalogue (154ms)

  4 passing (440ms)

A few things in these four tests are worth understanding before moving on:

  • describe, it and the hooks (before, beforeEach, afterEach, after) come from Mocha, and expect and should assertions come from Chai, both bundled with Cypress. You do not install them.

  • Commands retry. cy.contains(), cy.get() and the other queries keep looking for their element until it appears or the timeout passes. The default defaultCommandTimeout is 4000 ms, so a test that looks for something that never appears fails after about four seconds, not at once.

  • Many commands assert implicitly. cy.visit() fails on a non-2xx response or an uncaught application exception; cy.contains('text') fails if the text is not found, even without a .should(). You do not need an assertion after every command.

  • Page transitions are handled for you. After .click() navigates to a new page Cypress pauses the command queue until the next page has loaded, and raises the timeout to pageLoadTimeout (60 seconds by default) for that one page load. Commands never run against a stale or half-loaded page.

  • Do not chain after an action. The shape query, query, action is deliberate: a command that follows an action is not retried together with it, so put the next query on a new cy. chain, as the search test does with its second getByTestId.

  • type('quiet{enter}') types the characters and then presses Enter, which submits the search form. The {enter} is one of the special character sequences of .type().

A failing test

The official tutorial makes a test fail on purpose so that you can see what Cypress shows. The spec below looks for text that Bookshelf does not contain:

// cypress/e2e/failing-on-purpose.cy.ts
describe('A test that fails on purpose', () => {
  it('cannot find the text "hype"', () => {
    cy.visit('/');
    cy.contains('hype');
  });
});

The real output of cypress run:

  A test that fails on purpose
    1) cannot find the text "hype"

  0 passing (4s)
  1 failing

  1) A test that fails on purpose
       cannot find the text "hype":
     AssertionError: Timed out retrying after 4000ms: Expected to find content: 'hype' but never did.
      at Context.eval (webpack:///./cypress/e2e/failing-on-purpose.cy.ts:4:7)

  (Screenshots)

  -  .../cypress/screenshots/failing-on-purpose.cy.ts/A test that fails on purpose -- cannot
     find the text hype (failed).png     (1280x633)

The four seconds are the retry period of cy.contains(). The run also saved a screenshot of the failure; cypress run does that by default (screenshotOnRunFailure). In cypress open the same failure appears in red in the Command Log with a code frame, a stack trace and a clickable file link that opens your editor.

If you would rather record than type, Cypress Studio generates commands from your clicks. It is covered with debugging in Debugging, Studio, Plugins & Reporters.

Testing your own application

Cypress is built to run against an application you control, on a development server you start yourself. Three rules follow from that, all from the official guide:

  • Start the server before Cypress, and not from inside a test. Starting a web server from Cypress scripts is documented as an anti-pattern. Start it in a terminal or as a CI step, wait for it to answer, and then run Cypress.

  • Prefer a local server over production. A local server is one you can control: reset state, seed data, expose routes that exist only in the test environment and relax security features that make automation hard. Many teams run most tests locally and keep a small set of smoke tests for the deployed site.

  • Do not test sites you do not control. Third-party sites change without notice, run A/B tests and may block automation. The exceptions are sign-in providers and complementary services your application is designed to integrate with.

Set baseUrl so that a test never contains the host name, which is the first thing the guide asks you to configure:

// cypress/e2e/uses-base-url.cy.ts
describe('baseUrl', () => {
  it('prefixes cy.visit and cy.request', () => {
    cy.visit('/');
    cy.location('pathname').should('eq', '/');
    cy.request('/api/books').its('body.total').should('eq', 24);
    expect(Cypress.config('baseUrl')).to.match(/^http:\/\/localhost:\d+$/);
  });
});

The test passed (1 passing). Cypress restarts itself and closes the browser whenever you edit the configuration file, which is normal; click the spec again to relaunch.

To prepare data, the guide offers two tools: cy.task() to run Node.js code defined in setupNodeEvents, and cy.request() to call your own HTTP endpoints. Bookshelf has /api/test/reset and /api/test/seed for this, and the resetBookshelf task below uses the first. The other approach Cypress promotes, replacing server responses with cy.intercept(), is covered in Network, Stubs, Spies & Clocks, and signing in once per spec with cy.session() in Sessions, Authentication & Cross-Origin. Keeping tests independent of each other and of page markup is explained in Test Design & Page Objects.

cypress open and cypress run

Cypress has two modes, and the choice is the main difference between writing a test and running it in a pipeline.

Aspect cypress open (open mode) cypress run (run mode)

Purpose

Writing and debugging. The Launchpad, the Specs page and the Command Log with time travel.

Running to completion and exiting with a status. Pre-push checks and CI.

Browser

Visible (headed) always.

Headless by default; --headed shows it.

Specs

You click a spec; Cypress re-runs it whenever you save the file.

All specs matching specPattern, or the ones selected with --spec, one after another.

Defaults that differ

watchForFileChanges true, numTestsKeptInMemory 50, retries 0 (openMode).

watchForFileChanges false, numTestsKeptInMemory 0, retries 0 (runMode), screenshots on failure.

Output

The app window.

The terminal, plus screenshots, optional videos and any reporter.

Testing type

Chosen in the Launchpad or with --e2e / --component.

E2E by default; --component for component tests.

Headed and headless

cypress run launches the browser headless: no window is drawn, which is what CI needs. Pass --headed to see the browser, or --headless to make the default explicit. The header of the run output states what was used. These three commands were run:

npx cypress run --browser chrome --spec cypress/e2e/uses-base-url.cy.ts
#  │ Browser:        Chrome 155 (headless)

npx cypress run --browser chrome --headless --spec cypress/e2e/uses-base-url.cy.ts
#  │ Browser:        Chrome 155 (headless)

npx cypress run --browser chrome --headed --spec cypress/e2e/uses-base-url.cy.ts
#  │ Browser:        Chrome 155

cypress run --headed --no-exit keeps the browser open after the last spec has finished, so that you can look at the Command Log or open the browser’s developer tools. Headless runs use the same browser engine, but the window size and display configuration can differ between a laptop and a headless CI machine, so when a test passes headed and fails headless, compare the viewport first. The default viewport is 1000 by 660 (viewportWidth and viewportHeight); the failure screenshot above was 1280 by 633, so set the viewport explicitly when a test depends on the layout.

Choosing a browser

--browser accepts chrome, chromium, edge, firefox, electron (deprecated) and a name:channel form such as chrome:canary or firefox:nightly. A file system path points at a browser Cypress did not detect (only Chrome-family browsers and Firefox are supported). In open mode the Launchpad and a drop-down at the top right of the app select the browser. To make the choice permanent, set defaultBrowser in the configuration file, as the example config below does; it only affects the first launch of a project.

The flags of cypress run

These are the options of cypress run in Cypress 16. Only the ones named under "Ran" were executed for this page; the others are quoted from the reference.

Option Effect Ran

--browser, -b

Browser name or path. Without it, defaultBrowser, then Electron.

yes

--e2e / --component

Testing type. E2E is the default.

no

--spec, -s

Spec files to run: a path, a quoted glob such as "cypress/e2e/login/**/*", or several separated by commas. It can only narrow what specPattern already matches.

yes

--config, -c

Override configuration values: --config viewportWidth=1280,viewportHeight=720, or a quoted JSON object.

yes

--config-file, -C

Use a different configuration file.

yes

--env, -e

Secret values for cy.env(). Secrets given this way can appear in CI logs; prefer CYPRESS_* variables or your CI’s secret store.

no

--expose, -x

Public values for Cypress.expose(), for example --expose channel=smoke.

yes

--headed / --headless

Show or hide the browser.

yes

--no-exit

Keep Cypress open after the specs finish; combine with --headed.

no

--reporter, -r and --reporter-options, -o

A Mocha reporter, for example json or junit, and its options.

yes (json)

--quiet, -q

Print only the reporter output.

yes

--pass-with-no-tests

Exit with success when no spec is found (since 15.11.0).

yes

--posix-exit-codes

Exit with 1 on any failure instead of the number of failed tests.

yes

--project, -P

Run a Cypress project in another folder.

no

--port, -p

Override the port Cypress itself listens on.

no

--no-runner-ui / --runner-ui

Hide or force the Runner UI (relevant with Test Replay).

no

--record, --key, --parallel, --group, --ci-build-id, --tag, --auto-cancel-after-failures

Cypress Cloud options; see Cypress Cloud.

no

cypress open accepts --browser, --e2e, --component, --config, --config-file, --env, --expose, --port, --project, --global (one installation for several nested projects) and --detached. The flags you pass persist for every project you open until you quit.

Exit codes

By default the exit code of cypress run is the number of failed tests, with some reserved values, which is not POSIX compliant and can mislead: most shells apply modulo 256, so exactly 256 failures look like success. --posix-exit-codes makes the code 1 for any failure and also reports Cypress Cloud network failures as 112. With one failing test both modes returned 1; the difference only shows with two or more failures, which was not run.

npx cypress run --browser chrome --spec cypress/e2e/failing-on-purpose.cy.ts; echo $?
# 1
npx cypress run --browser chrome --posix-exit-codes --spec cypress/e2e/failing-on-purpose.cy.ts; echo $?
# 1
npx cypress run --pass-with-no-tests --spec cypress/e2e/nothing.cy.ts; echo $?
# 0
npx cypress run --spec cypress/e2e/nothing.cy.ts; echo $?
# 1

The JSON reporter and quiet output

npx cypress run --quiet --reporter json --spec cypress/e2e/uses-base-url.cy.ts
{
  "stats": {
    "suites": 1,
    "tests": 1,
    "passes": 1,
    "pending": 0,
    "failures": 0,
    "duration": 153
  },
  "tests": [
    {
      "title": "prefixes cy.visit and cy.request",
      "fullTitle": "baseUrl prefixes cy.visit and cy.request",
      "duration": 144
    }
  ]
}

The output above is shortened to the fields that matter. Reporters are covered in Debugging, Studio, Plugins & Reporters.

Name your scripts

Put the commands you type often in package.json. The official guide’s one rule is to never name a script cypress, because yarn cypress verify would then run your script instead of the Cypress CLI. Extra flags go after -- with npm:

{
  "scripts": {
    "cy:open": "cypress open",
    "cy:run": "cypress run",
    "cy:chrome": "cypress run --browser chrome",
    "typecheck": "tsc --noEmit"
  }
}
npm run cy:run -- --spec cypress/e2e/first-test.cy.ts

Both snippets were run in a freshly installed project: npm run cy:run -- --spec cypress/e2e/first-test.cy.ts ran the four tests of the first spec and npm run typecheck reported no errors.

The Cypress configuration file

The Launchpad writes cypress.config.ts (or .js, .mjs, .cjs, .mts, .cts) in the project root. It is a normal module that exports an object, which you should wrap in defineConfig() so that editors can complete and check it. The file below is the one used for every example on this page, and the test project that ran them was a plain folder with the layout shown under Folder structure:

// cypress.config.ts
import { defineConfig } from 'cypress';

const baseUrl = process.env.BASE_URL ?? 'http://localhost:3000';

export default defineConfig({
  // Electron is deprecated as a test browser: name an installed browser explicitly.
  defaultBrowser: 'chrome',
  video: false,
  e2e: {
    baseUrl,
    specPattern: 'cypress/e2e/**/*.cy.ts',
    supportFile: 'cypress/support/e2e.ts',
    setupNodeEvents(on, config) {
      on('task', {
        // Runs in Node, not in the browser: reset Bookshelf through its test API.
        async resetBookshelf() {
          const response = await fetch(`${baseUrl}/api/test/reset`, { method: 'POST' });
          return response.json();
        },
      });
      return config;
    },
  },
});

The file has three levels:

  • Top-level options apply to both testing types: defaultBrowser, video, the timeouts, the viewport, retries, screenshotsFolder, env, expose and so on.

  • The e2e object holds options for end-to-end tests only: baseUrl, specPattern, excludeSpecPattern, supportFile, slowTestThreshold, testIsolation, setupNodeEvents. A component object does the same for component tests, with devServer as its required option. An option in e2e or component overrides the same option at the top level for that type.

  • setupNodeEvents(on, config) is a function that runs in Node.js, in the background process, before the project loads, before the browser launches and while tests run. It registers listeners with on() (such as task, before:run, after:screenshot and before:browser:launch) and may return a modified config. Tests reach it through cy.task(). The resetBookshelf task above is called as cy.task('resetBookshelf') and its return value, the JSON the server answers, comes back to the test. Tasks run in Node.js, so they can read files and call databases, which a spec running in the browser cannot. The plugin events have their own page: Debugging, Studio, Plugins & Reporters.

The options you will meet first

Option Default Meaning

e2e.baseUrl

null

Prefix for cy.visit() and cy.request(). Set it, or every test repeats the host name.

e2e.specPattern

cypress/e2e/**/*.cy.{js,jsx,ts,tsx}

Glob (or array of globs) that decides which files are specs.

e2e.excludeSpecPattern

*.hot-update.js

Globs removed from the list of specs.

e2e.supportFile

cypress/support/e2e.{js,jsx,ts,tsx}

File loaded before every spec. false disables it.

e2e.testIsolation

true

Clear the page, cookies, local storage and session storage before each test (see Core Concepts).

defaultBrowser

null

Browser used when --browser is absent (since 13.16.0). Set it to an installed browser.

defaultCommandTimeout

4000

Milliseconds to retry most DOM commands.

pageLoadTimeout

60000

Milliseconds to wait for cy.visit(), cy.go(), cy.reload() and page transitions.

requestTimeout, responseTimeout, taskTimeout

5000, 30000, 60000

Waits for a request to leave, for a response, and for a cy.task() to finish.

viewportWidth, viewportHeight

1000, 660

Size of the application viewport; cy.viewport() changes it inside a test.

retries

{ runMode: 0, openMode: 0 }

Times to retry a failing test, separately for cypress run and cypress open.

video

false

Record a video of cypress run. Off by default.

screenshotOnRunFailure

true

Take a screenshot when a test fails in cypress run.

screenshotsFolder, videosFolder, downloadsFolder, fixturesFolder

cypress/screenshots, cypress/videos, cypress/downloads, cypress/fixtures

Where generated files go and where fixtures are read from.

trashAssetsBeforeRuns

true

Empty the screenshots, videos and downloads folders before each cypress run.

keystrokeDelay

null (no delay)

Delay between keystrokes of .type(). Since 16.0.0 there is none by default; the earlier default was 10 ms.

env, expose

{}

Secret values read with cy.env() and public values read with Cypress.expose().

projectId

null

Identifier for Cypress Cloud, written when you connect the project.

watchForFileChanges

open mode true, run mode false

Re-run a spec when its file changes.

This is a selection; the configuration reference lists every option, including the actionability options (waitForAnimations, animationDistanceThreshold, scrollBehavior), blockHosts, hosts, userAgent, chromeWebSecurity, includeShadowDom, redirectionLimit, numTestsKeptInMemory, manageBrowserMemory, the experimental flags and the certificate options (clientCertificates, trustedCertificates), which come back in Sessions, Authentication & Cross-Origin.

Overriding configuration

The same option can be set in six places, from the weakest to the strongest: its default, the configuration file, a cypress.env.json file (for env), a CYPRESS_* environment variable, a command line flag, and an override in the test. The Project settings panel of the app shows where each resolved value came from.

Where Example

Command line

cypress run --config viewportWidth=1280,viewportHeight=720 or cypress run --config-file tests/cypress.config.ts

Environment variable

CYPRESS_VIEWPORT_WIDTH=800 (the option name in upper snake case with the CYPRESS_ prefix)

Suite or test

it('shows a warning outside Chrome', { browser: '!chrome' }, () => { ... }) and describe('login', { retries: { runMode: 3, openMode: 2 } }, () => { ... })

Inside a test

Cypress.config('pageLoadTimeout', 100000) changes the setting for the rest of the current spec file only. blockHosts, viewportWidth and viewportHeight can no longer be changed this way during a test (16.0.0); use the test configuration or cy.viewport().

The following spec reads values that came from the command line and from the environment. It needs a secret and a public value, and shows the split Cypress 16 enforces:

// cypress/e2e/config-and-env.cy.ts
describe('Configuration, secrets and public values', () => {
  it('uses the viewport passed with --config', () => {
    cy.log(`isInteractive = ${Cypress.config('isInteractive')}`);
    cy.window().its('innerWidth').should('eq', Cypress.config('viewportWidth'));
  });

  it('reads a secret with cy.env() and a public value with Cypress.expose()', () => {
    cy.env(['READER_PASSWORD']).then(({ READER_PASSWORD }) => {
      expect(READER_PASSWORD).to.be.a('string').and.not.be.empty;
    });
    expect(Cypress.expose('channel')).to.equal('smoke');
  });
});
CYPRESS_READER_PASSWORD="$BOOKSHELF_READER_PASSWORD" npx cypress run --browser chrome \
  --spec cypress/e2e/config-and-env.cy.ts \
  --config viewportWidth=1280,viewportHeight=720 \
  --expose channel=smoke
  Configuration, secrets and public values
    ✓ uses the viewport passed with --config (26ms)
    ✓ reads a secret with cy.env() and a public value with Cypress.expose() (12ms)

  2 passing (52ms)

Cypress.env() no longer exists in Cypress 16: a CYPRESS_ variable is read with cy.env() (asynchronous, and only the keys you ask for are exposed to the page) and public values with Cypress.expose(). The migration, cy.origin() behaviour and the risks of the yielded value are covered in Sessions, Authentication & Cross-Origin.

ESM or CommonJS

Since Cypress 15.17.0 the configuration and everything setupNodeEvents imports are loaded as ECMAScript modules or as CommonJS using the same rules as Node.js, and there is no fallback to the other format when loading fails.

Config file Nearest package.json "type" Loaded as

.mjs or .mts

any

ES module

.cjs or .cts

any

CommonJS

.js or .ts

"module"

ES module

.js or .ts

absent or "commonjs"

CommonJS

The project that ran these examples is CommonJS ("type": "commonjs"), so import and export default in the .ts file are transpiled to require and module.exports. If you move to "type": "module", remember that __dirname does not exist in an ES module and import.meta.dirname takes its place.

Generated files are emptied before every run

With trashAssetsBeforeRuns on, cypress run removes the entire contents of the screenshots, videos and downloads folders at the start of each run, subfolders included, so a download you keep there is lost. On macOS and Windows items go to the system trash and on Linux they are deleted for good. On the Apple Silicon host used for this page the trash step failed on every run with the message below and the old screenshots piled up as (1), (2) copies; the run itself and its exit code were not affected. Keep nothing you need in those folders, and ignore the warning if you see it on a similar host.

Warning: We failed to trash the existing run results.

This error will not affect or change the exit code.

TypeScript

Cypress ships its own type declarations and understands TypeScript natively: specs, support files and the configuration file can all be .ts with no build step. Cypress transpiles specs itself, and since Cypress 15.0.0 it runs the configuration file with tsx (earlier versions used ts-node). It does not type-check; the editor and tsc do.

Install TypeScript 5, 6 or 7 (TypeScript 6.0 support arrived in 15.14.0 and TypeScript 7.0 in 15.19.0) together with the Node.js types, and create a tsconfig.json that limits the global types to the ones Cypress needs. Limiting types also avoids clashes when the project has Jest or another test runner that declares its own describe, it and expect:

{
  "compilerOptions": {
    "target": "es2022",
    "lib": ["es2022", "dom"],
    "sourceMap": true,
    "types": ["cypress", "node"]
  },
  "include": ["**/*.ts"]
}

The official page recommends es6 as target and library; a newer target works equally well and was used here. Without @types/node installed, TypeScript 7.0.2 stops with error TS2688: Cannot find type definition file for 'node' because "node" is listed in types; installing @types/node fixes it, after which npx tsc --noEmit reported no errors for the whole project (the specs, the configuration and the support files).

Custom commands need declarations

A command added with Cypress.Commands.add() is invisible to TypeScript until you declare it on the Cypress.Chainable interface. Put both in cypress/support/commands.ts, which the support file imports. The file must be a module, so end it with an empty export {}; otherwise declare global is an error. This is the command the first-test spec uses:

// cypress/support/commands.ts
declare global {
  namespace Cypress {
    interface Chainable {
      /** Select an element by its data-testid attribute. */
      getByTestId(id: string): Chainable<JQuery<HTMLElement>>;
    }
  }
}

Cypress.Commands.add('getByTestId', (id: string) => cy.get(`[data-testid="${id}"]`));

export {};
// cypress/support/e2e.ts
import './commands';

Other declarations follow the same pattern and are covered with custom commands and queries in Queries, Assertions & Custom Commands: child commands need the prevSubject option, Cypress.Commands.overwrite<'type', 'element'>() needs generics, custom queries are declared before addQuery(), properties your application adds to window extend Cypress.ApplicationWindow, and a standalone .d.ts file may declare namespace Cypress directly if it has no imports (when it does, use declare global). Make sure every tsconfig.json that should see a declaration file includes it.

If the editor does not pick up the types, restart its TypeScript server (in VS Code, the command "TypeScript: Restart TS Server").

For configuration files, defineConfig() infers the types of setupNodeEvents(on, config) automatically. For a function kept in another file, annotate it with Cypress.PluginConfig. Align compilerOptions.module with how the file loads: commonjs for a cypress.config.cts or a CommonJS project, NodeNext or ES2022 for cypress.config.mts or "type": "module".

Folder structure

Cypress prefers convention to configuration. For a TypeScript end-to-end project the Launchpad creates the files marked "created by the Launchpad" below; the rest are the ones this page added:

cy-getting-started/
  package.json
  cypress.config.ts            # created by the Launchpad
  tsconfig.json
  cypress/
    e2e/                       # specs; only files named *.cy.ts are run
      first-test.cy.ts
      failing-on-purpose.cy.ts
      uses-base-url.cy.ts
      config-and-env.cy.ts
    fixtures/
      example.json             # created by the Launchpad; load with cy.fixture()
    support/
      e2e.ts                   # created by the Launchpad; loaded before every spec
      commands.ts              # created by the Launchpad; custom commands live here
    screenshots/               # generated on failures and by cy.screenshot()
    downloads/                 # generated by downloads in a test
    videos/                    # generated when video is enabled

Component testing adds cypress/support/component.ts and cypress/support/component-index.html, and the specs sit next to the components. Every path is a default that can be changed in the configuration file; if you are starting a project, keep the defaults so that your layout matches the documentation.

Specs and the .cy. rule

Cypress loads only files that match specPattern, and the default pattern needs the .cy. infix. A file called cypress/e2e/login.ts is invisible; cypress/e2e/login.cy.ts is a spec. That convention lets helpers and specs live in the same folder without Cypress trying to run helpers. If a spec does not appear, the file name is the first thing to check. Three settings work together:

  • specPattern is the master list of what counts as a spec.

  • excludeSpecPattern subtracts files from that list.

  • --spec picks which of the remaining specs run this time. It can never add a file outside specPattern: a spec at tests/login.cy.ts is not found by --spec until specPattern also includes tests/**.

If specs still do not show up, run Cypress with DEBUG=cypress:cli,cypress:data-context:sources:FileDataSource,cypress:data-context:sources:ProjectDataSource to see how it searches.

As a rule of thumb use one spec for a feature or user flow, with describe() blocks for related scenarios. Cypress reloads the browser, the support file and the application for every spec, so many tiny specs are slower, and it distributes whole specs when you parallelize, so one very long spec delays the run.

Support files, fixtures and node events

  • The support file (cypress/support/e2e.ts) runs before every spec file. It is the place for custom commands, global beforeEach hooks and overrides. It is bundled together with everything it imports, so keep it lean and never import Node.js-only code such as fs into it.

  • Fixtures (cypress/fixtures) are static data loaded with cy.fixture(), with cy.intercept() { fixture: 'users.json' }, or attached to an upload with .selectFile(). Use cy.readFile() for files that change while the test runs and cy.task() for large files or work that needs Node.js.

  • Specs may be .js, .jsx, .ts or .tsx, may use ES modules or CommonJS and may import local modules and npm packages. The bundle runs in the browser, so a module that needs fs must run inside a task.

What to ignore in Git

The screenshots, videos and downloads are artefacts of each run, and the same applies to secrets in cypress.env.json:

# .gitignore
node_modules/
cypress/downloads/
cypress/screenshots/
cypress/videos/
cypress.env.json

IDE integration

Cypress works with any editor that understands JavaScript or TypeScript. Five steps give a fully configured editor:

  1. Turn on IntelliSense. With a tsconfig.json that lists "types": ["cypress"] (as above) every cy. command completes in VS Code, Visual Studio and WebStorm, which have TypeScript support built in; other editors need an extra TypeScript plugin. In a JavaScript project add /// <reference types="cypress" /> to the top of each spec, or a jsconfig.json with "include": ["./node_modules/cypress", "cypress/**/*.js"]. If completion fails, check that the right tsconfig.json covers the spec files, restart the TypeScript server, select the workspace TypeScript version and look for a clash with Jest types.

  2. Lint. The official eslint-plugin-cypress declares the Cypress and Mocha globals so that spec files do not report undefined variables, and has rules that flag anti-patterns while you type.

  3. Choose your file opener. The first time you click a file path or an error in the Command Log, Cypress asks where to open files: the file system, an IDE it found, or an application path. Change it later in the Settings tab of the app.

  4. Install extensions where useful. Community extensions add running specs from the editor and navigating to custom commands: Cypress Test Explorer, Cypress Helper and Test Utils for Visual Studio Code (and forks such as Cursor, which use the Open VSX registry, so some are missing there), the JetBrains "Test Automation" plugin (official) and Cypresso for JetBrains IDEs, and vim-test for Vim and Neovim. Commit a .vscode/extensions.json with the recommended extensions so that every teammate is offered them.

  5. Add AI tooling if you use an assistant. npx skills add cypress-io/ai-toolkit installs the Cypress AI Skills, and the Cloud MCP server connects an assistant to recorded runs. They are covered in AI Features in Cypress & Selenium and were not run for this page.

{
  "recommendations": [
    "dbaeumer.vscode-eslint",
    "dpanshug.cypress-test-explorer"
  ]
}

Debugging differs from what Playwright users may expect: a Cypress test runs in the browser, not in Node.js, so your editor’s Node debugger cannot attach to a test. Debug tests with the browser’s developer tools together with debugger, .debug() and cy.pause(). The editor’s Node debugger does apply to setupNodeEvents and to cy.task() code, which do run in Node.js. These tools are covered in Debugging, Studio, Plugins & Reporters. Cypress Studio also opens a code panel beside the running test, so you can edit a test while you watch it.

Common pitfalls

  • The binary is missing because a package manager blocked the postinstall script. Allow the script or run npx cypress install.

  • Running on Electron without noticing. Set defaultBrowser or pass --browser, and look at the Browser: line of the run output.

  • A spec that does not appear. The file name lacks .cy., or lives outside specPattern.

  • baseUrl not set. Every test then repeats the host name, and relative paths such as cy.visit('/') cannot resolve.

  • The server was not running when Cypress started. Cypress cannot start it for you; start it first and wait for it.

  • Chaining after an action. Commands after .click() or .type() on the same chain are not retried together with the action; begin a new cy. chain.

  • Assuming Cypress.env() still works. It was removed in 16.0.0; use cy.env() and Cypress.expose().

  • Expecting a 10 ms keystroke delay. Typing is instant since 16.0.0; set keystrokeDelay: 10 if your application needs slower input.

  • Secrets on the command line. --env values can appear in CI logs; use CYPRESS_* variables or the CI secret store.

  • TypeScript errors on custom commands. The declaration is missing, the file has no export {}, or the tsconfig.json that covers it does not list the declaration file.

  • Keeping files in the generated folders. They are emptied before every cypress run.

What changed recently

Version Change

16.2.0 (13 Oct 2026)

cy.getByRole() query. cypress run no longer changes the browser that cypress open selects by default for the project.

16.1.0 (15 Sep 2026)

trustedCertificates option for self-signed or private-CA certificates on Chrome, Chromium and Edge.

16.0.0 (1 Sep 2026)

Node.js 22.x, 24.x or 26.x and later required (20 and 25 dropped); bundled Node.js 24.15.0 and Electron 41.7.0. Electron deprecated as a test browser. Cypress.env(), cy.exec(), cy.end() and the execTimeout and allowCypressEnv options removed. Native network interception and HTTP/2 in Chrome, Chromium and Edge (forceHttp1 to opt out, deprecated). keystrokeDelay default 0. manageBrowserMemory on by default. visibilityStrategy: 'modern' by default. cypress info stops printing environment variables.

15.21.0

cypress tap commands (beta) for driving an open-mode session from a terminal or an agent; cy.exec() deprecated.

15.19.0

TypeScript 7.0 supported; Firefox 140 is the minimum.

15.17.0

Configuration and plugin files load as ES modules or CommonJS using Node.js rules, with no fallback.

15.14.0

TypeScript 6.0 supported.

15.11.0

--pass-with-no-tests flag for cypress run.

15.10.0

cy.env() and Cypress.expose() introduced; Cypress.env() deprecated.

15.0.0

tsx replaces ts-node for configuration files; TypeScript 5.0 or later required; Firefox automated with WebDriver BiDi only.

13.16.0

defaultBrowser option.