If you are adopting Playwright in a real project, the first thing you usually want is not a huge framework abstraction, it is a simple, trustworthy loop: install it, run a test locally, see the browser, fix the locator, and rerun. That local feedback loop is where a lot of Test automation quality is won or lost.

In this tutorial, I am going to walk through how I run Playwright tests locally, what gets installed, how the default project structure works, how to debug browser tests, and how to avoid the common traps that make local runs feel inconsistent. I will also touch on when a team might choose a simpler path, including Endtest as a low-code alternative for browser tests without owning Playwright code.

What it means to run Playwright locally

Running Playwright locally usually means three things:

  1. Installing the Playwright package and browser binaries on your machine
  2. Running one or more browser tests from your editor or terminal
  3. Debugging the test in a local browser session when it fails

That sounds straightforward, but the details matter. A local run can mean headless execution in your terminal, headed execution with a visible browser, or interactive debugging with Playwright Inspector and trace files. Each mode solves a different problem.

My rule of thumb, use headless for repeatable validation, headed for quick visual confirmation, and debug mode when a test fails for reasons that are not obvious from logs alone.

Prerequisites before you install Playwright

Before you run a Playwright tutorial project locally, make sure you have the basics in place:

  • A supported version of Node.js
  • A package manager, such as npm, pnpm, or yarn
  • A code editor, usually VS Code
  • Basic familiarity with terminal commands

If you are working in a shared repo, check the project’s README first. Many teams already have their own Playwright configuration, test fixtures, and scripts. In that case, you do not need to bootstrap a new project, you just need to install dependencies and run the existing tests.

Install Playwright locally

The easiest way to get started is to create or initialize a Playwright project using the official install flow. The project scaffolding adds the test runner, example tests, config files, and browser dependencies.

Option 1, create a new Playwright project

npm init playwright@latest

This is the path I recommend when you are learning Playwright or starting a fresh automation repo. The installer typically asks questions about JavaScript or TypeScript, whether you want GitHub Actions, and whether you want example tests.

Option 2, add Playwright to an existing project

If the repo already exists, install the package and browser binaries directly:

npm install -D @playwright/test
npx playwright install

That is the core of a Playwright install. The first command adds the Playwright test runner as a dev dependency. The second command downloads the browser binaries that Playwright uses for Chrome-based, Firefox, and WebKit testing.

If you only install the package and skip the browsers, your tests will fail later with missing browser errors. That is a common setup mistake, especially in monorepos or when teammates clone a repo and only run npm install out of habit.

What Playwright installs for you

When people say Playwright is “installed”, they often mean several separate things have been prepared:

  • The test runner package, usually @playwright/test
  • Browser binaries for Chromium, Firefox, and WebKit
  • Optional browser dependencies on Linux
  • A config file, often playwright.config.ts
  • Sample tests and test scripts in package.json

A typical package.json includes scripts like these:

{ “scripts”: { “test:e2e”: “playwright test”, “test:e2e:headed”: “playwright test –headed”, “test:e2e:ui”: “playwright test –ui” } }

Those scripts make it easier for the team to remember the right command without typing the full CLI each time.

A minimal Playwright test

Once the project is installed, a basic browser test is enough to verify the setup. Here is a small example in TypeScript:

import { test, expect } from '@playwright/test';
test('homepage loads', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/Example Domain/);
});

This test does three useful things:

  • Opens a browser page
  • Navigates to a URL
  • Asserts on something stable, in this case the title

If this works locally, you know the installation, browser download, and test runner are functioning together.

Run Playwright tests locally from the terminal

The most direct way to run Playwright browser tests locally is:

bash npx playwright test

That command runs all tests discovered by the runner, usually in headless mode. If everything is configured correctly, you will see a report in the terminal and any failing assertions or timeouts.

If you want to run a single test file, specify the path:

bash npx playwright test tests/example.spec.ts

If you want to run a single test by name, use the -g filter:

bash npx playwright test -g “homepage loads”

These commands are useful when you are iterating locally and do not want to wait for the whole suite.

Run tests in headed mode when you need to see the browser

Headless mode is great for repeatability, but sometimes you need to watch the app behave in a real browser window. That is where headed mode helps.

bash npx playwright test –headed

This is especially helpful when you are checking:

  • Whether a locator targets the right element
  • Whether a hover or dropdown interaction behaves as expected
  • Whether a page transition happens before a timeout
  • Whether the test is using the wrong viewport or browser context

If a test passes headless but fails headed, or vice versa, that is a clue that your test is relying on timing, animations, focus state, or browser-specific behavior.

Use the Playwright UI for local debugging

One of the best local workflows in Playwright is the interactive UI mode:

bash npx playwright test –ui

The UI helps you browse test files, run individual tests, inspect retries, and debug failures without repeatedly editing your command line. For a developer or SDET, this can shorten the feedback loop quite a bit.

If you are trying to understand why a test fails, also use debug mode:

bash npx playwright test –debug

Debug mode slows the test down, opens the inspector, and lets you step through actions one by one. That is often much better than adding temporary console.log statements everywhere.

Configure browsers, projects, and base URL

A local setup gets more useful when the config reflects your app. A typical playwright.config.ts might define a base URL, test timeout, and browser projects.

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({ use: { baseURL: ‘http://localhost:3000’, trace: ‘on-first-retry’ }, projects: [ { name: ‘chromium’, use: { …devices[‘Desktop Chrome’] } }, { name: ‘firefox’, use: { …devices[‘Desktop Firefox’] } } ] });

A few important notes here:

  • baseURL saves you from repeating the host in every test
  • trace: 'on-first-retry' captures useful debugging data without turning every local run into a heavy artifact process
  • Multiple projects help you catch browser-specific differences early

When you run locally, project configuration matters because some failures only happen in one browser or one viewport size.

Start the app before running tests

Playwright tests usually assume your application is already running locally. If your app is a web frontend, you often need to start it in another terminal first.

For example:

npm run dev

Then, in another terminal:

bash npx playwright test

If your app starts slowly, you may need to make the test command wait for the server. Playwright supports a webServer config for that.

webServer: {
  command: 'npm run dev',
  url: 'http://localhost:3000',
  reuseExistingServer: true
}

This is a practical detail that many teams overlook. A lot of “flaky local test failures” are really app startup timing problems, not Playwright problems.

Common local setup issues and how to fix them

Browser binaries are missing

If you see browser launch errors, run:

bash npx playwright install

If you are on Linux in a new environment, you may also need:

bash npx playwright install –with-deps

That installs OS-level dependencies needed by the browsers.

Node modules are out of sync

When a repo has been updated and your local environment is stale, clean reinstalling often helps:

rm -rf node_modules package-lock.json
npm install
npx playwright install

Use this carefully in shared repos, since package-lock handling depends on your team’s workflow. The point is simple, if dependencies drift, browser automation can break in ways that look like test failures but are really environment issues.

The app is not reachable

If tests fail with navigation timeouts, verify that the app is actually serving the expected port and path. A useful habit is to open the local URL in a regular browser before running the suite.

Locators are too brittle

Many local failures are locator problems, not execution problems. Prefer stable selectors over CSS paths that mirror the DOM too closely. For example, this is usually better than targeting structure:

typescript

await page.getByRole('button', { name: 'Sign in' }).click();

This tends to be more resilient than long CSS chains, especially as the UI evolves.

If your local test only works when the DOM is frozen in a specific state, it is probably too coupled to implementation details.

A practical local debugging workflow

When a Playwright browser test fails locally, I usually investigate in this order:

  1. Re-run the specific test, not the whole suite
  2. Open headed mode to watch the interaction
  3. Use --debug or UI mode to step through
  4. Inspect trace files if the test already captures them
  5. Check selector stability and timing assumptions

That sequence is simple, but it cuts through a lot of noise. The goal is to determine whether the issue is a broken test, a real application bug, or an environment mismatch.

Use trace viewer and screenshots when needed

Playwright’s tracing features are one of the best reasons to run browser tests locally instead of only in CI. If you enable traces, you can inspect the exact sequence of actions, network events, and DOM snapshots.

A simple config option can help:

use: {
  trace: 'on-first-retry',
  screenshot: 'only-on-failure',
  video: 'retain-on-failure'
}

For local work, I like trace and screenshot capture because they answer the question, “what did the browser actually see?” That is often more useful than just reading stack traces.

Local runs versus CI runs

Running Playwright locally and in CI are related, but they are not the same discipline. Local runs are for iteration and diagnosis. CI runs are for consistency, enforcement, and signal quality in a pipeline.

In the context of continuous integration, the suite should prove that the application still works after code changes. Locally, you are trying to understand behavior quickly enough to fix issues without guesswork.

A healthy workflow usually looks like this:

  • Developers run a targeted subset locally before committing
  • The full Playwright suite runs in CI
  • Failures in CI get reproduced locally with the same test and browser

If local and CI behave very differently, look for missing environment variables, different viewport sizes, headless-only behavior, or missing test fixtures.

Keeping Playwright tests maintainable locally

A test suite that is easy to run locally is usually easier to maintain overall. A few practices help a lot:

Keep tests independent

Each test should set up its own state as much as possible. If you depend on another test to create data, local reruns become fragile.

Use stable test data

Avoid relying on shared accounts or mutable records unless the test explicitly cleans up after itself. Local debugging is much easier when the test can be rerun repeatedly with the same result.

Keep setup close to the test intent

If every test has to read through a large abstraction layer before you understand what it is doing, debugging becomes slower. Small helper functions are fine, but over-abstracted page objects can hide useful detail.

Match your app’s auth strategy

Login is a common source of local friction. If you can use storage state or a dedicated test account, it often saves time. Just make sure the auth state does not expire in a way that makes local tests confusing.

When local Playwright is not the right answer

Playwright is excellent when your team can own code, dependencies, and browser execution details. It is less ideal when the people who need to author or maintain tests are not primarily developers.

That is where a simpler platform can make sense. For teams that want browser coverage without maintaining Playwright code, Endtest is a relevant alternative because it uses low-code, agentic AI workflows and platform-native steps rather than asking everyone to manage a full code-based test stack. I would still choose Playwright for a code-heavy engineering team, but I would not force it on a broader QA or product group if the maintenance cost is too high.

A basic local checklist I use

When I set up a new repo or troubleshoot an existing one, I check the following:

  • node_modules is installed cleanly
  • Playwright browser binaries are installed
  • The app is reachable on the expected local URL
  • The config file sets the right base URL and browser projects
  • Tests can run in headless mode
  • Headed mode works for debugging
  • Trace or screenshot artifacts are available for failures

If all of that works, the local loop is usually good enough to support productive test development.

Example: run one browser across one file

Sometimes you do not want the whole cross-browser matrix locally. You only want to validate one path in Chromium while you are developing the test.

bash npx playwright test tests/login.spec.ts –project=chromium

That is a practical compromise. It keeps local feedback fast while preserving broader browser coverage for CI.

Example: focus on a single failed test with trace data

If a test is failing intermittently, rerun it with retries and traces enabled. In local work, this can make the difference between guessing and knowing.

bash npx playwright test tests/cart.spec.ts –retries=1

If the first run fails and the retry passes, you have a signal that timing, environment, or test isolation may be involved. Do not ignore that pattern, because it often becomes a CI flake later.

Final thoughts

To run Playwright tests locally is really to set up a feedback loop you can trust. Install the package, install the browsers, point Playwright at your app, and make sure you can run the same test headless, headed, and in debug mode. Once that works, you are in a much better position to write stable browser tests, diagnose failures quickly, and decide when a code-based framework is the right fit.

If you are deep in a developer-owned test stack, Playwright is a strong choice. If your team wants browser testing without owning all the code and infrastructure around it, a platform like Endtest may be worth evaluating alongside your Playwright tutorial research.

For readers comparing approaches, I also recommend looking at the broader tradeoffs in Playwright vs Selenium in 2026, especially if your team is deciding whether to standardize on a newer framework or keep an existing automation strategy.

The most important part, though, is not the framework itself. It is making local execution fast enough, clear enough, and repeatable enough that your team actually uses it.