Playwright Codegen is a tool that records your browser interactions and generates Playwright test code based on those actions.

Let’s see it in action. Imagine you want to test logging into a website. You’d start by running npx playwright codegen. This opens a browser window alongside a Playwright Inspector window.

Here’s a common scenario: logging into a demo e-commerce site.

First, open the target URL in the browser window that codegen launched.

https://www.saucedemo.com/

Now, interact with the page as a user would. Type "standard_user" into the username field. As you do this, the Playwright Inspector will automatically generate the corresponding code:

page.locator("#user-name").fill("standard_user")

Next, type "secret_sauce" into the password field:

page.locator("#password").fill("secret_sauce")

Click the login button:

page.locator("#login-button").click()

And that’s it! You’ve just recorded a basic login test. The generated code is ready to be copied and pasted into your test file. You can continue interacting with the page, and codegen will keep adding to the script.

The core problem Playwright Codegen solves is the initial friction of writing test code. Instead of manually figuring out selectors and writing Playwright’s API calls, you can visually interact with your application and get working code instantly. This dramatically speeds up test creation, especially for repetitive or complex user flows.

Internally, codegen works by listening to browser events (like clicks, key presses, and form submissions). When an event occurs, it analyzes the DOM to find a robust selector for the element involved. It then translates the event into the appropriate Playwright API call (e.g., page.locator(...).fill(...), page.locator(...).click()). It prioritizes stable selectors like IDs, data-testid attributes, or text content over fragile ones like generic CSS classes or XPath.

The generated code isn’t always perfect, and you’ll often want to refactor it. For instance, codegen might pick a selector like page.locator("div:nth-child(2) > input"). While this might work now, it’s brittle. If the HTML structure changes slightly, your test will break. A better selector would be page.locator("[data-test='username']") if the application uses data attributes for testing. You can manually edit the generated code to use more resilient selectors.

You can also instruct codegen to use different types of selectors. By default, it tries to find the best one. If you want to enforce a particular strategy, you can use the --selector flag. For example, npx playwright codegen --selector text will prioritize selectors based on visible text. This can be useful if your application heavily relies on text labels for elements.

One aspect that often surprises people is how codegen handles dynamic content or asynchronous operations. It doesn’t magically "wait" for things to appear. Instead, Playwright’s underlying actions, like click() or fill(), have built-in auto-waiting mechanisms. When codegen generates page.locator("#login-button").click(), Playwright automatically waits for the button to be visible, enabled, and stable before attempting the click. This is why the generated code often appears to handle waiting implicitly, even though it’s the Playwright API itself doing the heavy lifting.

The next step after generating code is understanding how to integrate it into your project, manage test data, and implement more advanced assertions beyond simple clicks and fills.

Want structured learning?

Take the full Playwright course →