Resource library

QA How-To

Playwright video recording: Examples and Best Practices

Explore Playwright video recording examples for failures, retries, custom contexts, file handling, CI artifacts, privacy, and debugging best practices.

19 min read | 3,256 words

TL;DR

Configure `use.video` in Playwright Test, usually with `retain-on-failure` or a retry-focused mode. For a manual context, set `recordVideo`, close the Page or context, then use the associated Video object to save, locate, or delete the finalized recording.

Key Takeaways

  • Use Playwright Test video modes for retry-aware recording and runner-managed attachments.
  • Choose a mode based on whether the initial failure, a retry, or every run contains useful evidence.
  • Match recording dimensions to the viewport so important UI evidence remains readable.
  • Await Page or BrowserContext closure before expecting a manual recording to be finalized.
  • Treat every popup Page as a separate potential video rather than one desktop-wide recording.
  • Pair video with traces or screenshots only when each artifact answers a distinct debugging question.
  • Use synthetic data, restricted access, and explicit retention for videos stored by CI.

Playwright video recording examples are most useful when they answer two questions: which test run should be recorded, and how will the team retrieve the recording after a failure? In Playwright Test, the usual answer is a video option such as retain-on-failure or on-first-retry. In the library API, create a browser context with recordVideo, close the page or context, and then save the resulting Video.

Recording every run is easy, but it is rarely the best default for a large CI suite. Good video policy balances diagnostic value, storage, privacy, retry behavior, viewport fidelity, and artifact retention. This guide gives runnable TypeScript patterns for both Playwright Test and the lower-level library API, then turns them into a maintainable team policy.

TL;DR

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

export default defineConfig({
  retries: 1,
  use: {
    video: 'on-first-retry',
    trace: 'on-first-retry',
  },
});
Goal Recommended starting mode Why
Diagnose ordinary CI failures retain-on-failure Records every run, keeps failed runs only
Investigate retry-only flakiness on-first-retry Limits recording to the first retry
Keep evidence for every retry on-all-retries Preserves retry behavior without recording initial passes
Audit every execution on Keeps every video, with the highest storage cost
Run locally with no artifacts off Avoids recording overhead and sensitive files

Videos are finalized when their page or browser context closes. For most suites, configure recording centrally and let Playwright Test attach artifacts to the test result. Use page.video() only when you need programmatic control over a manually created context.

1. Understand what Playwright records

Playwright records the rendered viewport of each Page in a video-enabled BrowserContext. It does not record the whole desktop, browser chrome, DevTools, native operating system dialogs, or activity in other applications. That boundary is valuable: the artifact follows the automated page and is less noisy than a desktop screen recording.

There are two recording layers. Playwright Test exposes the use.video option and manages context creation, result attachments, output paths, and retention modes. The Playwright library exposes browser.newContext({ recordVideo }) and a Video object for each Page. Prefer the test-runner option when tests use @playwright/test; it aligns the recording with retries and reports. Use recordVideo when building a custom runner, a diagnostic utility, or a manually controlled context.

A video shows visible behavior over time, which is excellent for overlays, unexpected redirects, animation, mis-clicks, and multi-step user flows. It does not expose DOM snapshots, request payloads, console messages, or locator resolution. Pair it with a trace when those details matter. The Playwright timeout troubleshooting guide is a useful companion because a video alone often shows where the test stopped, while a trace explains what Playwright was waiting for.

Remember the lifecycle rule: the encoder completes after the Page closes. A path lookup or copy may wait until then. If a custom script exits without awaiting context.close(), the video can be missing or incomplete even though the interactions ran successfully.

2. Configure Playwright video recording examples by mode

The simplest production configuration records only evidence that the team is likely to inspect:

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

export default defineConfig({
  testDir: './tests',
  outputDir: 'test-results',
  retries: process.env.CI ? 2 : 0,
  use: {
    baseURL: 'http://127.0.0.1:4173',
    video: 'retain-on-failure',
  },
});

off records nothing. on records and keeps every run. retain-on-failure records each run but removes recordings for successful runs. on-first-retry records only the first retry, while on-all-retries records every retry. Current Playwright also supports policies that retain the first failure or retain failures and retries. Choose a mode from the installed Playwright version's TestOptions.video type rather than copying an unsupported value into an older project.

Retry semantics matter. The initial execution is the first run, and retry executions follow it. With one retry and on-first-retry, a test that passes immediately has no video. A test that fails once and passes on retry has one video from the retry, not the original failure. If the first failure itself is the evidence you need, use retain-on-failure or the appropriate retain-first-failure policy.

Do not set retries merely to produce a recording. Retries are a release and reliability decision. If a critical test should fail once and stop, retain-on-failure records that failure without changing its result semantics.

3. Match video size to viewport and evidence

When no explicit size is supplied, Playwright derives a recording size from the viewport and fits it within its documented default bounds. An explicit size makes artifacts consistent across developer machines and CI workers:

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

export default defineConfig({
  use: {
    viewport: { width: 1280, height: 720 },
    video: {
      mode: 'retain-on-failure',
      size: { width: 1280, height: 720 },
    },
  },
});

Matching viewport and video dimensions avoids unnecessary scaling and makes small text easier to inspect. A lower recording resolution reduces file size, but it can make validation messages, table cells, and subtle layout shifts unreadable. Select the smallest resolution that still preserves the evidence your failures require.

Video size does not change the application viewport. It controls the encoded output. If the aspect ratios differ, the viewport is scaled to fit and positioned within the video frame. That can produce unused space or make a responsive failure look smaller than expected. Set viewport separately and treat it as test behavior, then select a compatible video size as artifact behavior.

Mobile projects deserve their own choice. A phone viewport recorded into a landscape frame wastes space. Keep the emulated device settings from Playwright's device descriptor and use a recording size with a similar portrait aspect ratio when readability matters. Do not use video configuration to simulate a device. Device behavior comes from viewport, user agent, touch, device scale factor, and related context options.

4. Apply recording policy by project and suite

One global policy may be too expensive or too weak. Project-level configuration can record a critical browser while using retry-only evidence elsewhere:

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

export default defineConfig({
  retries: 1,
  use: { video: 'on-first-retry' },
  projects: [
    {
      name: 'chromium-critical',
      use: {
        ...devices['Desktop Chrome'],
        video: 'retain-on-failure',
      },
    },
    {
      name: 'webkit-regression',
      use: {
        ...devices['Desktop Safari'],
        video: 'on-first-retry',
      },
    },
  ],
});

For a small suite that always needs a recording, a file-scoped override is direct:

import { test, expect } from '@playwright/test';

test.use({ video: 'on' });

test('completes a regulated approval flow', async ({ page }) => {
  await page.goto('/approvals/42');
  await page.getByRole('button', { name: 'Approve' }).click();
  await expect(page.getByRole('status')).toHaveText('Approved');
});

Keep overrides visible near the suite boundary. A helper that silently enables recording makes cost and privacy difficult to review. Name projects around business purpose or environment, not only around artifact mode.

Parallel execution does not require custom filenames under Playwright Test. Each test result owns its output directory and attachments, including retry and project identity. Hand-built paths based only on the test title can collide when projects or retries run concurrently. Let the runner manage standard recordings, and use testInfo.outputPath() for other per-test artifacts.

5. Record a manual browser context with recordVideo

The library API is appropriate when you own context creation. This script is runnable after installing playwright and its Chromium browser:

// record-checkout.ts
import { mkdir } from 'node:fs/promises';
import { chromium } from 'playwright';

await mkdir('videos', { recursive: true });

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 1280, height: 720 },
  recordVideo: {
    dir: 'videos',
    size: { width: 1280, height: 720 },
  },
});

const page = await context.newPage();
await page.setContent(`
  <main>
    <h1>Checkout</h1>
    <button onclick="this.textContent='Order placed'">Place order</button>
  </main>
`);
await page.getByRole('button', { name: 'Place order' }).click();
await page.getByRole('button', { name: 'Order placed' }).waitFor();

await context.close();
await browser.close();

Run it with a TypeScript runtime or compile it with your project settings. The key behavior is not the runner command, it is await context.close(). Closing finalizes recordings for every Page in that context.

The dir is mandatory for recordVideo. Playwright creates uniquely named files within it. Treat those names as internal unless you explicitly save a copy under a business-friendly name. Do not assume a Page writes to a filename based on its URL or title.

A manually created context inside a Playwright Test test is not managed like the built-in context fixture. You own its closure even when an assertion fails. Wrap it in try/finally, or build a fixture that closes it during teardown, so failure paths still finalize evidence.

6. Save, locate, and delete a Video object safely

page.video() returns the Video associated with the Page, or null when recording is disabled. Capture the object while the Page is available, close the Page, then use saveAs() or path():

import { mkdir } from 'node:fs/promises';
import { chromium } from 'playwright';

await mkdir('artifacts', { recursive: true });
const browser = await chromium.launch();
const context = await browser.newContext({ recordVideo: { dir: 'videos' } });
const page = await context.newPage();

await page.setContent('<h1>Receipt 814</h1>');
const video = page.video();
if (!video) throw new Error('Video recording was not enabled');

await page.close();
await video.saveAs('artifacts/receipt-814.webm');
console.log(await video.path());

await context.close();
await browser.close();

saveAs() waits for the video to finish if necessary. Do not await it while the Page is intentionally left open forever, because finalization depends on closure. The local path() API is useful for diagnostics but is not supported for every remote-browser connection. saveAs() expresses the portable intent more clearly when you need a named copy.

Use await video.delete() when policy says the recording must be discarded. This also waits for recording to finish. Avoid a save-then-delete sequence unless two separate storage locations are intentional, because saveAs() creates a copy while delete() removes the original recording.

In normal Playwright Test usage, avoid reaching into the fixture Page's video during afterEach and waiting for finalization. Fixture teardown occurs after test hooks, so such code can wait on a Page that the runner has not closed yet. Let the runner attach its video, or create and own a separate context when programmatic naming is a hard requirement.

7. Handle popups and multiple pages without losing evidence

Context recording is page-based. When a test opens a popup or new tab in the same video-enabled context, that Page has its own Video object. The main page's recording is not a desktop montage of every tab.

import { mkdir } from 'node:fs/promises';
import { chromium } from 'playwright';

await mkdir('videos', { recursive: true });
await mkdir('artifacts', { recursive: true });

const browser = await chromium.launch();
const context = await browser.newContext({ recordVideo: { dir: 'videos' } });
const page = await context.newPage();
await page.setContent(`
  <button onclick="window.open('data:text/html,<h1>Invoice 42</h1>')">
    Open invoice
  </button>
`);

const popupPromise = page.waitForEvent('popup');
await page.getByRole('button', { name: 'Open invoice' }).click();
const invoice = await popupPromise;
await invoice.getByRole('heading', { name: 'Invoice 42' }).waitFor();

const mainVideo = page.video();
const invoiceVideo = invoice.video();
await Promise.all([page.close(), invoice.close()]);
await Promise.all([
  mainVideo?.saveAs('artifacts/main.webm'),
  invoiceVideo?.saveAs('artifacts/invoice.webm'),
]);

await context.close();
await browser.close();

The Playwright popup and new tab examples explain event-first tab capture in depth. Video does not replace that synchronization. Register the popup wait before the action, assert the destination, and save each recording only if separate evidence is actually useful.

For Playwright Test managed contexts, reports may contain more than one video attachment when several Pages were recorded. Train reviewers to select the Page that contains the failing behavior rather than assuming the first attachment is the active tab.

8. Add video annotations for faster review

Current Playwright Test versions can annotate videos with action highlights and test information. This is especially helpful when a click target is small or when several steps look visually similar:

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

export default defineConfig({
  use: {
    viewport: { width: 1280, height: 720 },
    video: {
      mode: 'on-first-retry',
      size: { width: 1280, height: 720 },
      show: {
        actions: {
          duration: 700,
          position: 'top-right',
          fontSize: 14,
        },
        test: {
          level: 'step',
          position: 'top-left',
          fontSize: 12,
        },
      },
    },
  },
});

Annotations are a diagnostic aid, not part of the application. Do not write visual assertions against them. Choose positions that do not cover the application's most important controls, and keep the overlay readable at the encoded resolution.

Use test.step() names that describe business actions if the overlay displays step information. A label such as Submit valid refund request is more useful than Step 3. Do not include passwords, tokens, customer names, or other sensitive values in step titles because titles can appear in reports and video overlays.

This configuration is version-sensitive because annotation options are newer than basic video recording. Keep Playwright and its browsers upgraded together, run npx playwright test --list or a small smoke test after config changes, and let TypeScript reject unsupported property names. Do not silence a config type error with as any.

9. Choose video, trace, screenshot, or all three

Artifacts answer different debugging questions. Enabling everything for every run can create more noise than evidence.

Artifact Best question answered Main limitation Practical policy
Video What did the user-visible flow look like over time? No DOM, locator, or network detail Failures or retries
Trace Which action, locator, DOM snapshot, or network event failed? Larger and needs Trace Viewer First retry or failure
Screenshot What was visible at one failure point? No timeline Only on failure
Console or network logs Which browser or service error occurred? Can expose secrets and lacks visual state Filtered, failure-focused capture

A balanced configuration is often:

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

export default defineConfig({
  retries: 1,
  use: {
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    trace: 'on-first-retry',
  },
});

This preserves the original failure video and screenshot, then gathers a richer trace if the test retries. It does not guarantee the retry reproduces the same failure, so teams investigating intermittent initial-run failures may choose a failure-retention trace mode too.

Video is least useful for a pure API test with no Page, a backend data assertion, or a hidden network race. It is highly useful for overlays, navigation loops, responsive layout, focus changes, and visual timing. Select evidence from the failure model, not from a blanket desire to capture more.

10. Publish recordings as CI artifacts responsibly

Playwright writes managed outputs beneath the configured output directory, normally test-results. The HTML reporter can link attachments when the files remain available to the report. A CI system may delete the workspace after the job, so upload the report and relevant test results using that CI provider's artifact mechanism.

Keep artifact upload separate from test execution. The test should create evidence and return the correct pass or fail result. A CI post-job step should upload artifacts even when the test command exits nonzero. Do not catch test failures merely to make the upload step run.

Retention should be explicit. Recordings can contain email addresses, order details, internal URLs, feature names, or personal data typed into forms. Use synthetic accounts and sanitized fixtures. Restrict artifact access, encrypt storage through the CI platform, and choose a deletion window that matches organizational policy. Avoid public links for private test reports.

Budget storage using actual measurements from your suite rather than a fabricated industry average. Run representative tests across browsers, observe total artifact size, and multiply by normal build frequency and retention. Encoding size depends on resolution, duration, motion, number of Pages, and application content.

Use unique run identifiers from CI when copying artifacts to durable storage. Do not rely on branch name plus test title alone, because reruns can overwrite evidence. Preserve test, project, retry, commit, and build identity in metadata even when the physical video filename is opaque.

11. Design recordings for diagnosable tests

A video can reveal weak test design but cannot repair it. Add business-level test.step() boundaries, stable locators, and web-first assertions so the recording has a comprehensible story. If a test clicks five unlabeled icons and waits silently for 30 seconds, the video will be difficult to interpret even when every frame is available.

Keep setup proportional. Logging in through the UI before every test lengthens videos and hides the failing feature behind repetitive footage. Reuse a safe authenticated storage state when the login screen is not under test. The feature recording then begins closer to the relevant behavior.

Use explicit assertions after significant transitions. Video review is a secondary diagnostic process, not an oracle. The automated result should still say whether the receipt number, route, toast, or data row was correct. For network-dependent failures, combine UI evidence with targeted response assertions or routing. The Playwright route fulfill examples show how to control external boundaries without inventing sleep time.

Avoid slowing actions solely to make recordings cinematic. Playwright's actionability and assertions provide synchronization. Artificial waitForTimeout() calls increase suite duration and can conceal races. If humans cannot see a fast transition, use trace snapshots or action annotations rather than production sleeps.

Finally, reproduce a known failure and confirm the chosen policy actually retains its video. A configuration review is not enough. Verify the file appears in the report, survives CI upload, can be played by the team's tools, and is removed according to retention policy.

12. Review Playwright video recording examples and best practices

Use this review checklist before standardizing recording across a repository:

  1. Decide whether evidence is needed from the initial failure, retries, or every run.
  2. Configure use.video centrally and keep narrow overrides visible.
  3. Match recording dimensions to the viewport and readability requirement.
  4. Close every manually created context in a failure-safe teardown.
  5. Treat each popup Page as a separate recording source.
  6. Let Playwright Test manage standard paths and retry identity.
  7. Pair video with trace or screenshots only when each answers a distinct question.
  8. Keep secrets and customer data out of pages, step names, URLs, and artifacts.
  9. Upload results on failed CI jobs and set access plus retention controls.
  10. Test the evidence pipeline with a deliberate sample failure.

For a new suite, start with retain-on-failure, one readable recording size, and restricted CI artifact storage. If videos are too numerous, measure whether on-first-retry still captures the failures your team sees. If failures disappear on retry, preserve initial failure evidence instead of assuming retry-only recording is sufficient.

Do not optimize solely for file count. A tiny artifact policy that never captures the defect is waste, while recording every successful smoke test is also waste. Review a month of actual incidents, classify which artifact resolved each one, and revise the policy using that evidence.

Interview Questions and Answers

Q: How do you enable video recording in Playwright Test?

Set the video option under use in playwright.config.ts, for example video: 'retain-on-failure'. Playwright Test creates the context, records eligible runs, and attaches retained files to the result. I choose the mode based on whether the initial failure or retry is the most useful evidence.

Q: What is the difference between use.video and recordVideo?

use.video is Playwright Test policy with retry-aware recording and retention. recordVideo is a BrowserContext creation option in the library API. With recordVideo, the caller owns directory management, Page access, context closure, and any named copies.

Q: Why can a video be missing after a custom script finishes?

Video encoding completes when the Page or BrowserContext closes. If the script exits without awaiting context.close(), finalization may not finish. I put manual contexts in failure-safe teardown and await closure before exiting.

Q: When would you choose on-first-retry over retain-on-failure?

I use on-first-retry when failures are expected to reproduce on retry and storage needs to stay low. I use retain-on-failure when evidence from the original failed run matters, especially if retries often pass. The retry policy and artifact policy must be designed together.

Q: How do you save a video with a meaningful filename?

For a manually owned recording context, I get page.video(), close the Page, and call video.saveAs() with a unique output path. In Playwright Test, I normally keep runner-managed attachments because they already preserve project, retry, and test identity.

Q: Does one video include every tab in a test?

No. Recording is associated with individual Pages in the video-enabled context. A popup or new tab can have its own Video, so I capture the Page explicitly and select or save the relevant recording.

Q: Why keep a trace when a video already exists?

Video shows the visual timeline, while a trace provides actions, locator information, DOM snapshots, and network context. They answer different questions. I enable both only when the failure model justifies the extra artifact volume.

Q: What security risks do test videos introduce?

Videos can expose typed credentials, personal data, internal URLs, and unreleased UI. I use synthetic data, restrict artifact access, avoid secrets in step titles, and define retention. Recording configuration is part of the test-data security review.

Common Mistakes

  • Enabling video: 'on' for every large CI suite without measuring storage or review value.
  • Choosing on-first-retry while expecting it to preserve the initial failed execution.
  • Creating a context with recordVideo and forgetting to await context.close().
  • Calling saveAs() while leaving the recorded Page open indefinitely.
  • Assuming the main Page video contains popup and new-tab footage.
  • Using a recording size with an incompatible viewport aspect ratio.
  • Building shared filenames that collide across browsers, workers, or retries.
  • Waiting in afterEach for a fixture Page that closes only during later teardown.
  • Adding fixed sleeps so humans can follow the video.
  • Treating video as a substitute for assertions, traces, or network diagnostics.
  • Uploading videos only on a successful CI job path.
  • Capturing production credentials or customer data in retained artifacts.
  • Copying newer annotation options into an older Playwright version and hiding type errors.

Conclusion

The strongest Playwright video recording examples use runner-managed modes for ordinary tests and recordVideo only for contexts the code truly owns. Start recording before the flow, preserve the run that contains useful evidence, close the Page or context to finalize it, and let assertions determine correctness.

Adopt one failure-focused configuration, reproduce a sample failure, and verify the recording reaches the report with appropriate access and retention. Then adjust modes, resolution, annotations, and companion traces using evidence from real debugging sessions.

Interview Questions and Answers

How would you configure failure-focused video recording in Playwright Test?

I set `use.video` to `retain-on-failure` in the shared config. This records each run but keeps videos only for failed runs, including a failed run that later passes on retry. I verify that the reporter and CI artifact step preserve the retained output.

Explain the difference between `video` and `recordVideo` in Playwright.

`video` is a Playwright Test option with modes tied to test runs and retries. `recordVideo` is a BrowserContext option in the lower-level library API. With `recordVideo`, my code owns the directory, lifecycle, closure, and any named copy of the resulting file.

Why must a manual BrowserContext be closed for video recording?

The recording is finalized when its Page or context closes. If a custom script exits without awaiting closure, the encoder may not finish and the file can be incomplete or absent. I place context closure in failure-safe teardown.

How do retries influence the video mode you select?

Retry-only modes record retry executions, not necessarily the initial failure. If a defect disappears on retry, I preserve initial failed-run evidence with a failure-retention mode. I keep retry configuration as a reliability decision rather than adding retries only to obtain artifacts.

How would you retrieve a custom Page video?

I call `page.video()` while recording is enabled and guard against a null result. After closing the Page, I use `saveAs()` for a meaningful unique path or `path()` in a supported local connection. I avoid waiting for fixture Page finalization from an `afterEach` hook.

What does a Playwright video fail to show?

It does not provide DOM snapshots, locator resolution, complete network payloads, or browser console causality. It also records the Page viewport rather than the entire desktop. I pair it with a trace or targeted logging when those details are necessary.

How do multiple tabs affect Playwright video artifacts?

Each Page can have its own recording in the video-enabled context. I capture the popup or tab through the correct event, assert its destination, and retain its Video only when it adds evidence. I do not assume the opener's recording includes other Pages.

What privacy controls would you require for test videos?

I use synthetic accounts and redact sensitive test inputs before recording. CI artifacts need access control, encryption through the platform, and a documented retention window. Test titles and step annotations must also avoid secrets because they can appear in overlays and reports.

Frequently Asked Questions

How do I record a video in Playwright Test?

Set `video` under `use` in `playwright.config.ts`, for example `video: 'retain-on-failure'`. Playwright Test creates eligible recordings, applies the selected retention policy, and exposes retained files as test result attachments.

What is the best Playwright video mode for CI?

`retain-on-failure` is a strong default when evidence from the initial failure matters. `on-first-retry` reduces artifacts but records the first retry rather than the original failed run, so choose it only when retries usually reproduce the problem.

Why is my Playwright video missing?

A manual recording is finalized only after its Page or BrowserContext closes. Await `context.close()` and ensure the process does not exit early. In Playwright Test, also confirm the configured mode keeps that run and that CI uploads the output on failed jobs.

How do I save a Playwright video with a custom filename?

For a manually created video-enabled context, get `page.video()`, close the Page, and call `video.saveAs()` with a unique path. Runner-managed recordings are usually better left as attachments because Playwright already preserves test, project, and retry identity.

Does Playwright record popups in the same video?

Each Page in a video-enabled context has its own Video object. A popup or new tab is not merged into one desktop recording with the opener, so capture the popup Page and select its recording when that is the relevant evidence.

Can I control Playwright video resolution?

Yes. Supply a `size` in the Playwright Test video object or in `recordVideo` for a manual context. Keep its aspect ratio compatible with the viewport to avoid unnecessary scaling and unreadable detail.

Should I enable both video and trace in Playwright?

Use both when they solve different failure questions. Video shows visible behavior over time, while a trace adds actions, DOM snapshots, locator context, and network detail. Failure-focused or retry-focused policies limit the artifact volume.

Are Playwright test videos safe to publish?

Not automatically. Videos can contain typed credentials, personal data, internal URLs, and unreleased features. Use synthetic data, restrict access, avoid public artifact links, and enforce an organizational retention policy.

Related Guides