tldr: Playwright codegen records your clicks in a browser window and emits test code as you go, in JavaScript, Python, Java, or C#. It is the fastest way to learn Playwright's API and a legitimate way to scaffold a locator you cannot guess. It is not a way to build a suite: recorded tests capture what the page looked like on recording day, and they decay from the first refactor onward.
The playwright codegen command opens a browser and a recorder side by side:
npx playwright codegen demo.playwright.dev/todomvc
Everything you do in the browser becomes code in the recorder window: navigations, clicks, fills, and the assertions you add from its toolbar. Copy the output into a spec file and you have a runnable test without writing a line.
What the recorder is for, and what it is emphatically not for, is the subject of the top community thread on this topic ("Is Playwright codegen useful?"). Both halves of that answer are below. Commands here were verified against the Playwright 1.62.1 CLI.
The flags that matter
Codegen's usefulness doubles once you know its options:
# emit Python instead of JavaScript
npx playwright codegen --target python demo.playwright.dev/todomvc
# write straight to a file
npx playwright codegen --output recorded.spec.ts demo.playwright.dev/todomvc
# record in a device profile
npx playwright codegen --device="iPhone 13" demo.playwright.dev/todomvc
# set the viewport
npx playwright codegen --viewport-size="800,600" demo.playwright.dev/todomvc
Emulation flags mirror the test runner: --color-scheme=dark, --timezone="Europe/Rome", --geolocation="41.890221,12.492348", --lang="it-IT". Recording in the same profile your tests run under matters more than it looks; a locator recorded against the desktop layout can point at an element the mobile layout never renders.
Recording behind a login
The most common real blocker is that the flow you want to record sits behind authentication. The storage-state pair solves it in two passes:
# pass 1: log in by hand, then close the browser
npx playwright codegen --save-storage=auth.json myapp.example.com
# pass 2: recording starts already authenticated
npx playwright codegen --load-storage=auth.json myapp.example.com

auth.json captures cookies, localStorage, and IndexedDB. It is a real credential artifact, so gitignore it. The same file plugs into the test runner as storageState, which means the login you recorded once can authenticate the whole suite, the same pattern API-seeded auth uses.
Why recorded tests do not survive
Codegen picks good locators; the recorder explicitly prioritizes role, text, and test-id lookups over brittle CSS paths. The decay problem is not locator quality. It is that a recording is a snapshot of one path through one build of your UI, chosen by what the recorder could see, not by what the test should mean.
Record a checkout today and the output encodes today's DOM: this button label, this field order, this modal. Ship a redesign and the recording has no intent to fall back on, only stale coordinates in page-structure space. The suite that took an afternoon to record takes every following sprint to re-record, which is how teams end up with a folder of recorded-*.spec.ts files nobody trusts. Self-healing automation exists specifically to patch this failure mode after the fact, and natural-language engines like Passmark avoid it by storing intent and re-resolving elements at run time instead of at recording time.
The one-sentence version: codegen writes down where things were, and a durable test needs to express what should happen.
What codegen is actually good for
Three jobs, all real:
Learning the API. Watching your clicks become getByRole and getByLabel calls is the fastest Playwright tutorial that exists. Ten minutes of recording teaches locator style better than an afternoon of docs.
Scaffolding a locator you cannot guess. For a gnarly widget, record the one interaction, steal the locator codegen chose, and throw the rest away.
One-off reproductions. A bug report with a recorded script attached beats prose steps, and nobody maintains a reproduction after the fix lands.
The discipline that keeps codegen useful is treating its output as raw material. Extract the locators into page objects or fixtures, rewrite waits as web-first assertions, and delete the recording.
FAQs
What is Playwright codegen?
The built-in test recorder: npx playwright codegen <url> opens a browser plus an inspector window, and your interactions stream out as runnable test code with locators and assertions. It ships with Playwright, no extra install.
How do I record a test behind a login?
Record once with --save-storage=auth.json, log in manually, and close the browser. Then record with --load-storage=auth.json to start authenticated. Keep the file out of version control, since it holds live session cookies.
Can codegen output Python or Java?
Yes. --target accepts the language: python, python-async, java, csharp, or the JavaScript/TypeScript default. The recording session is identical; only the emitted code changes.
Why do recorded tests break so often?
Because a recording captures one build's page structure with no memory of intent. UI changes strand the recorded steps, and there is nothing to heal from except re-recording. Recorded output is durable only after a human rewrites it around what the flow is supposed to prove.
Should I build my test suite with codegen?
No. Use it to learn, to scaffold locators, and to attach reproductions to bug reports. Suites built from raw recordings turn into a re-recording treadmill after the first redesign. Extract what the recorder taught you into structured tests, then discard the recording.
