TL;DR: A Selenium to Playwright migration is mostly a locator and wait-pattern swap, not a rewrite. Most WebDriverWait blocks collapse into a single auto-waiting Playwright locator call. Playwright has grown to over 52 million npm downloads a month in 2026, per the Playwright Market Share report, and most of that growth comes from teams who want parallel execution without standing up a Grid. This guide shows the actual code changes, step by step, plus which parts of the migration ContextQA automates for you.
A migration touches three layers of your test code: how you find elements, how you wait for them, and how you assert against them. Everything else, your test scenarios, your data setup, your reporting, stays the same. That's the part most teams underestimate: you are translating an API, not redesigning your test suite.
Quick answers
Is a Selenium to Playwright migration mostly a rewrite? No. Explicit waits collapse into auto-waiting locators, assertions move to expect(), and your test scenarios and data setup stay the same. The rewrite happens at the API layer, not the test-design layer.
Can I migrate gradually instead of all at once? Yes, and you should. Run both suites in the same CI pipeline during the transition, port your highest-value flows first, and retire Selenium suites in waves once Playwright coverage matches.
Do I need to change programming language to use Playwright? No. Playwright ships official bindings for JavaScript, TypeScript, Python, Java, and .NET, so a Java Selenium suite can move to Java Playwright without a language change.
Locators and waits, before and after
Selenium does not know when an element is ready, so you wrap almost every interaction in an explicit wait. Playwright locators retry automatically until the element is attached, visible, and stable, so the wait disappears into the call itself.
Before (Selenium):
await driver.wait(until.elementLocated(By.css('#submit-btn')), 10000);
const button = await driver.findElement(By.css('#submit-btn'));
await driver.wait(until.elementIsVisible(button), 5000);
await button.click();
After (Playwright):
await page.locator('#submit-btn').click();
One line instead of four. The click() call waits for the element to exist, be visible, and stop animating before it acts, which is why Playwright suites report far fewer timing-related flakes than Selenium suites running the same scenarios.
Assertions, before and after
Selenium has no built-in assertion library, so most suites pull in Chai, Jest, or a similar tool just to compare values. Playwright ships its own expect(), and it behaves differently in a way that matters.
Before (Selenium + Chai):
const text = await driver.findElement(By.css('.status')).getText();
expect(text).to.equal('Success');
After (Playwright):
await expect(page.locator('.status')).toHaveText('Success');
The Selenium version reads the text once and compares it immediately, so it fails if the status label hasn't updated yet. Playwright's expect() retries the assertion against the live DOM until it passes or the timeout runs out, which removes an entire category of race-condition flakes without adding a manual wait.

Parallel execution, before and after
Selenium itself runs one browser at a time. To parallelize, you provision Selenium Grid: a hub plus a pool of browser nodes that your tests connect to remotely.
Before (Selenium Grid):
const driver = new Builder()
.usingServer('http://selenium-hub:4444/wd/hub')
.forBrowser('chrome').build();
That one line of config hides an entire piece of infrastructure you now own: a hub, a node pool, and whatever keeps them patched and scaled.
After (playwright.config.ts):
export default defineConfig({
fullyParallel: true,
workers: process.env.CI ? 4 : undefined,
});
No hub, no nodes. Playwright spins up its own worker processes on whatever machine runs the suite, locally or in CI, and splits test files across them (see the official Playwright parallelism docs). For larger suites, --shard splits the run itself across multiple CI machines on top of that.
What ContextQA automates in this migration
The code translation above is mechanical, but doing it across a few hundred Selenium tests by hand is where migrations stall. ContextQA's platform reads your existing Selenium locators and steps and converts them into Playwright-equivalent, self-healing selectors, so a CSS or XPath locator that breaks after a front-end change repairs itself instead of failing the build.
It also runs the migrated suite in parallel out of the box, across browsers and environments, without you writing the playwright.config.ts sharding logic yourself, and flags which failures are genuine regressions versus leftover timing assumptions carried over from the old Selenium waits.

A realistic migration timeline
Treat this as a staged rollout, not a weekend project. Most teams move through it in this order:
1. Audit your suite. Inventory which Selenium tests still matter, which are duplicates, and which cover flows that no longer exist.
2. Port one critical flow first. Pick your highest-value path, usually login or checkout, and migrate it completely so your team learns the pattern translations on something that matters.
3. Map the recurring patterns. Waits, locators, and assertions repeat across a suite. Once you've translated them once, the rest is largely find-and-replace.
4. Run both suites in parallel in CI. Don't cut over until Playwright coverage matches Selenium coverage on the same flows.
5. Retire Selenium in waves. Decommission by suite or by team, not all at once, so a gap in coverage is never more than one wave wide.
Skip the line-by-line translation work. ContextQA converts your Selenium suite into self-healing Playwright-equivalent tests automatically, then runs them in parallel from day one. See how the platform handles migrations or book a demo with your own test suite.
Bottom line
The code changes in a Selenium to Playwright migration are smaller than most teams expect; the real work is sequencing them so nothing breaks mid-transition. For the strategic case on whether to migrate at all, read our Selenium to Playwright migration guide, and if you're still weighing frameworks entirely, see Playwright vs Selenium vs Cypress in 2026.