Playwright and Cypress are the two most popular tools for end-to-end web testing. Both are good. Both are used by serious companies. But they work in different ways, and the differences matter when you choose what to learn or what to put in a project.
The code examples use an invented reading list app. You can practise test writing in the TestAcademy editor, which uses different apps. If you also want Selenium in the picture, the three-way comparison is here.
The short answer
Learn Playwright first if you are starting today. Ordinary JavaScript syntax, free parallel runs, and Safari-engine coverage.
Keep Cypress if your team already runs it. It is an excellent tool, and migrating costs more than most teams expect. Move only when you hit a limit you can name.
The same test in both tools
These examples use an invented reading list app. They are separate from TestAcademy assessment problems.
Playwright:
import { test, expect } from '@playwright/test';
test('adds a book to the reading list', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Add to list' }).first().click();
await expect(page.getByTestId('reading-count')).toHaveText('1');
});
Cypress:
describe('Reading list', () => {
it('adds a book to the reading list', () => {
cy.visit('/');
cy.get('[data-testid="add-to-list"]').first().click();
cy.get('[data-testid="reading-count"]').should('have.text', '1');
});
});
Notice the style difference. Playwright uses normal async/await JavaScript. Cypress uses a command chain: each cy command goes into a queue and runs in order. Both read well. They feel very different once tests get complex.
The queue is the thing that catches people. In Cypress, this does not do what it looks like it does:
// Wrong. cy.get() queues a command, it does not return the element.
const count = cy.get('[data-testid="reading-count"]');
if (count.text() === '1') { /* never works */ }
You work with the value inside the chain instead:
cy.get('[data-testid="reading-count"]').should('have.text', '1');
If you arrive from Playwright and try to await a cy command, or arrive from Cypress and forget to await a Playwright one, that is the single biggest source of early confusion.
How they work inside
Almost every difference below comes from one architectural choice.
Cypress runs inside the browser. Your test executes in the same browser tab as the app, in the same event loop. That gives it direct access to the page, the network layer and application state. It is also why its time-travel runner is so good: it is right there when each command runs.
Playwright controls the browser from outside over a websocket connection. It is further from your app, but it is not bound by what one tab can do.
The consequences are practical:
- Multiple tabs. A flow that opens a new tab is ordinary in Playwright and awkward in Cypress.
- Multiple origins. Cypress has
cy.origin(), so cross-domain testing is possible, but it is more work than in Playwright, where a redirect to a payment provider is just a redirect. - Browser contexts. Playwright gives each test an isolated context in milliseconds, which is how it parallelises cheaply.
Feature comparison
| Topic | Playwright | Cypress |
|---|---|---|
| Language style | async/await | command chain |
| Browsers | Chromium, Firefox, WebKit | Chrome family, Firefox, WebKit (experimental) |
| Test languages | JS/TS, Python, Java, C# | JS/TS only |
| Multiple tabs | Built in | Limited |
| Multiple origins | Built in | Via cy.origin() |
| Auto-waiting | Yes | Yes, via retry-ability |
| Parallel runs | Free, built in | Free locally, CI orchestration is paid |
| Component testing | No | Yes |
| Network mocking | page.route() |
cy.intercept() |
| Mobile emulation | Yes | No |
| Debugging | Trace viewer, UI mode | Time-travel runner |
| First release | 2020 | 2017 |
Browser support, precisely
Playwright ships its own builds of Chromium, Firefox and WebKit. WebKit is the engine behind Safari, so you get Safari-engine coverage on Windows and Linux. It is the engine, not Safari itself, so a Safari-specific bug can still get through.
Cypress supports Chrome-family browsers and Firefox properly. Its WebKit support is described in Cypress's own documentation as experimental, and it is built on Playwright's WebKit. If Safari coverage is a requirement rather than a nice-to-have, that is a real difference.
Waiting
Both wait automatically, which is the main reason both are more pleasant than older tools.
Playwright checks that an element is attached, visible, stable and enabled before acting on it, and its assertions retry until they pass or time out.
Cypress retries commands and assertions in the same spirit. The mental model differs slightly: Cypress retries the last command in a chain, which is why where you put .should() matters.
Neither one prevents flaky tests. Bad selectors and shared state break both. Fixing flaky tests covers the causes, and waiting in Cypress, Playwright and Selenium is the full reference for the rules in each.
Debugging
This is close, and each wins a different situation.
Cypress wins the interactive loop. The runner lists every command as it happens. Hover a step and the app snaps back to that moment. For learning what your test is actually doing, nothing else is quite as immediate.
Playwright wins the failure you cannot reproduce. Turn on tracing and a failed run gives you DOM snapshots before and after every action, the network log, the console and a screenshot timeline, all in a viewer you can open offline:
npx playwright show-trace trace.zip
That is the difference between fixing a CI-only failure in ten minutes and spending an afternoon adding logs. Playwright's UI mode covers the interactive side too, and it has closed much of the gap.
Network mocking
Both can intercept requests, and both are good at it.
// Cypress
cy.intercept('GET', '**/api/products', { body: [] }).as('products');
// Playwright
await page.route('**/api/products', (route) => route.fulfill({ json: [] }));
Because Cypress lives inside the browser, its network layer feels slightly more direct, and cy.intercept() with aliases and waiting is very pleasant. Playwright's version is equally capable and works across contexts and tabs.
Component testing
This one is not close. Cypress mounts React, Angular, Vue and Svelte components directly in a real browser and lets you test them in isolation. Playwright's component testing has stayed experimental.
If you want one tool for both component and end-to-end tests, that is a genuine reason to choose Cypress.
Parallel runs and cost
Both tools are free and open source. The difference is what happens in CI.
Playwright runs tests in parallel across workers by default, and sharding across several CI machines is configuration rather than a purchase.
Cypress runs specs in parallel locally if you set it up. Splitting a suite across CI machines with balanced timing and a merged report is Cypress Cloud, which is paid above a free tier. For a small suite this never comes up. For a suite that takes forty minutes, it becomes a budget line.
Speed
Playwright is usually faster in like-for-like comparisons, mostly because of architecture: one browser process, a cheap fresh context per test, and parallelism by default.
Be careful with benchmark posts, including the ones published by either project. They tend to compare a tuned version of one tool against a naive version of the other. In practice your suite's runtime is decided by how often you log in through the UI, how much data each test creates, and how well you parallelise. Those dwarf the framework's own overhead.
Job market
Postings for QA automation engineers usually name Selenium, Cypress or Playwright, often more than one. Playwright demand has grown quickly since 2023. Cypress remains common at companies that adopted it earlier and had no reason to move.
The useful thing to know is that the concepts transfer almost one to one. Locators, waiting, assertions and test isolation are the same ideas in both. Someone fluent in one picks up the other in days.
Should you migrate?
Usually, no. "Playwright is more modern" is not a business case, and a rewrite spends real weeks.
Migrate when you can name the limit you are hitting:
- You need Safari-engine coverage and cannot rely on experimental support.
- Your flows cross tabs or domains and
cy.origin()has become a maintenance burden. - Your CI bill for parallel orchestration is growing.
- Your team wants tests in Python, Java or C#.
If you do move, do it gradually. Run both suites side by side, write new tests in Playwright, and port old ones only when you touch them. Cypress to Playwright is a command-by-command translation guide.
Common mistakes when choosing
- Choosing by hype. Look at your project: which browsers, how many domains, which languages your team writes.
- Mixing the mental models. Do not
awaitacycommand. Do not forget toawaita Playwright one. - Expecting the tool to fix flakiness. It will not. Good locators and clean test data do.
- Migrating for the syntax. The syntax is the cheapest part of a suite to learn and the most expensive to rewrite.
Frequently asked questions
Is Playwright replacing Cypress? No. Playwright has grown faster, but Cypress is actively developed and is stronger at component testing and interactive debugging.
Which is easier for a beginner? Cypress usually has a friendlier first hour, because the runner shows you what is happening. Playwright's syntax is more transferable, since it is plain JavaScript rather than a command chain.
Can Playwright test Safari? It tests WebKit, the engine behind Safari, on any operating system. It does not drive Safari itself. Only Selenium does that, on macOS.
Is Cypress free? The test runner is free and open source. Cypress Cloud, which handles parallel orchestration and reporting in CI, is paid above a free tier.
Can I use both? Yes, and teams often do during a migration. Run them side by side and port tests as you touch them.
Remember this
Cypress runs inside the browser. That buys the best interactive debugger and component testing, and it costs you tabs, origins and Safari.
Playwright drives from outside. That buys multi-tab, multi-origin, WebKit and free parallel runs, and it asks you to write ordinary async JavaScript.
Learn Playwright first if you are choosing today. Keep Cypress if your team already runs it and nothing is blocking you. And the best way to decide is to write the same test twice: open the practice editor and solve one problem in each.