PRACTICAL GUIDE / playwright monorepo architecture

Playwright Monorepo Architecture with Project Graphs, Shards, and Unified Reports

Structure Playwright in a monorepo with package-owned suites, project dependencies, balanced CI shards, complete blob artifacts, and one unified report.

By The Testing AcademyUpdated July 11, 20269 min read
All field guides
In this guide10 sections
  1. Establish one repository-level execution contract
  2. Keep package ownership inside a root topology
  3. Model setup with project dependencies
  4. Separate the project matrix from the shard matrix
  5. Resolve one stable inventory before sharding
  6. Treat blob output as an interchange artifact
  7. Verify completeness before merging
  8. Normalize paths and environment identity
  9. Handle setup and shard failures operationally
  10. Support focused local work without fragmenting CI

What you will learn

  • Establish one repository-level execution contract
  • Keep package ownership inside a root topology
  • Model setup with project dependencies
  • Separate the project matrix from the shard matrix

A monorepo Playwright design succeeds when package teams can own scenarios independently while CI still executes one coherent inventory and publishes one trustworthy result. Separate package commands without a common project and artifact contract produce duplicated setup, inconsistent filters, and reports that cannot answer whether the repository passed.

Use projects to describe execution policy and dependencies, shards to distribute the resolved inventory, and blob artifacts to transport results. Keep these three axes separate. Their composition should be explicit at the root even when test code remains package-owned.

Establish one repository-level execution contract

Choose the command, configuration root, browser installation, environment inputs, and reporter policy that define an official run. Package scripts may offer focused developer shortcuts, but CI should not discover a different Playwright world in every workspace.

The Playwright projects guide defines a project as a logical group of tests using the same configuration and supports project dependencies for setup work. In a monorepo, project names become report and artifact identity, so keep them stable and meaningful.

Animated field map

Monorepo Execution and Reporting Flow

Package-owned suites enter one project graph, CI partitions the resolved inventory, blob artifacts preserve results, and a verified merge publishes repository quality.

  1. 01 / package suites

    Package test suites

    Teams own scenarios, fixtures, tags, and stable source locations.

  2. 02 / project graph

    Project dependency graph

    Root policy selects suites, browsers, setup nodes, and environments.

  3. 03 / shard matrix

    CI shard matrix

    Each job runs one unique partition of the same resolved inventory.

  4. 04 / blob artifacts

    Blob result artifacts

    Every shard retains results, traces, screenshots, and identity.

  5. 05 / quality report

    Unified quality report

    Completeness checks precede merge and repository-level publication.

Keep package ownership inside a root topology

Each product package should own its test directory, domain fixtures, test data contracts, and CODEOWNERS rules. The root owns project registration, shared timeouts, artifact policy, and supported browser or environment matrix. Shared fixture libraries belong in a test-support package with an explicit public API, not through relative imports across product internals.

A simple topology keeps generated evidence outside source packages:

Example
apps/
  storefront/e2e/
  backoffice/e2e/
packages/
  test-support/src/
playwright/
  setup/
  report/
playwright.config.ts
test-results/
blob-report/

Use one resolved @playwright/test dependency and browser installation for the official run. Multiple runner copies can disagree about configuration or report format. Autonomous release trains may justify independent configs, but then define a higher-level reporting protocol rather than pretending those runs form one native project graph.

Model setup with project dependencies

Project dependencies make authentication or environment setup visible to reporters and traces. Consumer projects should depend only on the setup they require. Do not make every package wait for a global setup that provisions unrelated products.

TypeScript
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: '.',
  fullyParallel: true,
  outputDir: 'test-results',
  reporter: process.env.CI ? [['blob']] : [['html', { open: 'never' }]],
  use: {
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    {
      name: 'storefront-auth-setup',
      testDir: 'playwright/setup',
      testMatch: /storefront-auth\.setup\.ts/,
    },
    {
      name: 'storefront-chromium',
      testDir: 'apps/storefront/e2e',
      dependencies: ['storefront-auth-setup'],
      use: { ...devices['Desktop Chrome'] },
      metadata: { owner: 'storefront' },
    },
    {
      name: 'backoffice-chromium',
      testDir: 'apps/backoffice/e2e',
      use: { ...devices['Desktop Chrome'] },
      metadata: { owner: 'backoffice' },
    },
  ],
});

Use names that remain stable when CI jobs move. Avoid embedding shard numbers in projects; the runner adds shard identity to result artifacts. If setup creates mutable shared state, make it idempotent and safe when multiple shard jobs execute the same dependency.

Separate the project matrix from the shard matrix

Projects answer what configuration a test runs under. Shards answer which portion of the selected inventory a machine executes. A browser-package-environment cross product can grow quickly, so include only combinations backed by product risk and ownership.

Do not create projects named shard-1, shard-2, and so on. That duplicates CI partitioning in source configuration and makes local runs confusing. Likewise, do not use CI shards to represent different browsers while calling them one partition; the resulting merged report hides missing configuration coverage.

When a package needs only one browser in pull requests and a broader matrix later, select projects at the command or workflow level. Keep the project definitions stable so report history and ownership do not change with pipeline tier.

Resolve one stable inventory before sharding

Every shard job must use the same revision, lockfile, generated files, configuration, project filters, grep filters, and shard total. Only the current shard index should differ. A package generated after partitioning or a conditional test directory can cause duplicate or missing execution.

With fullyParallel: true, Playwright can distribute individual tests more evenly. Without it, sharding uses file-level granularity, which may be preferable while files still share mutable setup. Enabling full parallelism is an architecture decision: first remove ordering assumptions, module-level mutable state, and shared accounts.

Use unique one-based indexes from the CI matrix:

Shell
npx playwright test \
  --shard="${SHARD_INDEX}/${SHARD_TOTAL}" \
  --reporter=blob

Keep SHARD_TOTAL identical across jobs and record both values in job metadata. If the selected inventory is small, more shards can add setup cost without shortening the critical path. Choose the count from observed execution and environment capacity rather than package count.

Treat blob output as an interchange artifact

The Playwright reporters documentation describes blob reports as detailed test-run output designed for later report generation and shard merging. Upload each shard's entire blob directory under a unique CI artifact name. Do not publish separate HTML reports as the repository result.

Artifact upload must run after test failure unless the job is cancelled. The failing shard contains the errors, traces, and screenshots reviewers need. Use a retention policy appropriate for test data, and exclude authentication state or other secrets from broad artifact globs.

Do not edit or unzip blob files before merge. Keep source checkout and dependency context available to the merge job so locations and attachments resolve consistently.

Verify completeness before merging

A merge command cannot know that a CI job vanished unless the pipeline checks. Produce a small, non-sensitive shard manifest beside each blob artifact containing run ID, revision, shard index, shard total, project filter, and command identity. The merge job must see every expected index exactly once and reject revision or filter mismatches.

This check prevents a dangerous outcome: three available artifacts merge successfully even though the fourth shard never uploaded. The HTML can look complete while silently omitting coverage. Missing evidence is a pipeline failure, regardless of whether available test results are green.

After verification, place blobs in one directory and run:

Shell
npx playwright merge-reports --reporter=html ./all-blob-reports

Publish the merged report only after the completeness gate. Preserve the raw blobs long enough to regenerate a different supported reporter without rerunning tests.

Normalize paths and environment identity

Shards should run from the same repository root with the same testDir. Different working directories can make source locations and snapshot paths inconsistent. When merging results from genuinely different operating systems, use an explicit merge configuration and tag those environments so the report does not imply identical render conditions.

Avoid absolute machine paths in custom attachments. Use testInfo.outputPath() for unique output owned by the test, and attach through the runner so the blob reporter captures it. Package fixtures should not write to one shared filename, because parallel workers and shards can overwrite it before upload.

Give test titles enough domain context to be useful when merged. Source path and project identity disambiguate duplicates technically, but a report full of tests named loads page forces reviewers to open every entry.

Handle setup and shard failures operationally

Disable fail-fast cancellation across shard jobs when the objective is complete regression evidence. A setup dependency failure should be visible as setup failure, with dependent tests not misreported as product passes. Upload setup traces from the shard where they ran.

The merge job should run when shard jobs fail, but not when the workflow is cancelled before artifacts are coherent. It should publish a failed unified report and return a failing status when any test or completeness check failed. Report publication is not the same as pipeline success.

When one package's environment is unavailable, do not remove its project from the inventory after the run begins. Mark the infrastructure failure explicitly. Dynamic omission converts an outage into apparently reduced scope without an accountable result.

Support focused local work without fragmenting CI

Developers need fast package commands. Provide wrappers that call the root config with --project, a source path, or a stable tag. These are views of the same graph, not alternate configurations with different fixture and reporter behavior.

Track ownership in project metadata, path conventions, or test annotations and surface it in custom reporting. Ownership should route failures, not decide whether results are merged. Repository quality still needs one index where a reviewer can filter by package, project, tag, and status.

Use this architecture checklist:

  • Package teams own scenarios and fixtures while the root owns execution policy.
  • Project nodes express real configurations and setup dependencies, never shards.
  • Every shard resolves the same revision, inventory, filters, and shard total.
  • Parallel mode is enabled only after tests and data are independent.
  • Blob artifacts upload on failure and exclude authentication material.
  • A manifest gate proves all expected shard outputs are present and compatible.
  • One merged report preserves package, project, shard, source, and attachment identity.

Test the pipeline by forcing a failed scenario, a failed setup project, and a missing shard artifact. The monorepo design is complete when each case produces one honest report and a failing repository signal, while a normal run lets every package retain ownership without losing the system-wide view.

// LIVE COURSE / THE TESTING ACADEMY

Playwright Automation Mastery

Go beyond Selenium. Master Playwright with JS/TS in 90 days.

From the instructor behind this guide.

Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.

Code PROMODE / 10% offJoin the batch

The Testing Academy editorial desk

Practical QA guidance built around test evidence, production tradeoffs, and interview-ready explanations.

Published July 11, 2026 / Reviewed July 11, 2026

PRIMARY REFERENCES

Verify the details at the source

QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.

  1. 01
    Playwright documentation

    Microsoft

    Canonical API, locator, fixture, browser, and test-runner behavior.

  2. 02
    Playwright best practices

    Microsoft

    Official guidance for resilient tests, isolation, and user-facing locators.

  3. 03
    WebDriver standard

    W3C

    The browser automation protocol specification used by major automation stacks.

FAQ / QUICK ANSWERS

Questions testers ask

Should every monorepo package have its own Playwright configuration?

Package configs can work for autonomous products, but one root config is stronger when suites share projects, setup dependencies, sharding, and report merging. Keep package ownership through test directories and metadata.

What is the difference between a Playwright project and a CI shard?

A project defines a logical test configuration and dependency node. A shard partitions the selected test inventory across machines; it should not encode browser, package, or environment policy.

Why use blob reports for Playwright monorepo shards?

Blob reports retain detailed results and attachments in a mergeable format. A downstream job can combine every shard into one HTML or other supported report without rerunning tests.

How can a merge job avoid publishing an incomplete green report?

Record the expected shard and project inventory, upload artifacts even after test failure, verify every expected blob is present, and mark missing artifacts as pipeline failure before publishing.

When should fullyParallel be enabled for monorepo sharding?

Enable it after tests are independent at test level. It improves shard balance, but it can expose file-local shared state and ordering assumptions that file-level distribution previously hid.