How to Take a Playwright Screenshot of an Angular Page After Navigation
Capture an Angular page after navigation with Playwright by waiting for the UI state you need, then saving a viewport or full-page screenshot.
Use page.goto() to navigate, wait for a locator that proves the Angular content you need is visible, and then call page.screenshot(). Navigation reaching the load event does not guarantee that Angular has finished fetching data or updating the page.
import { test, expect } from '@playwright/test';
test('captures the rendered Angular page', async ({ page }) => {
await page.goto('http://localhost:4200');
// Choose a stable element that proves the content for this screenshot is ready.
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
await page.screenshot({ path: 'screenshot.png', fullPage: true });
});
Replace the URL and heading with your app’s route and a visible element that represents the state you intend to capture. Playwright locators and web-first assertions wait and retry for the condition, so the screenshot follows the rendered UI rather than an arbitrary delay. See the Page API, locators guide, and navigation guide.
1. Set up Playwright
If your project already uses Playwright Test, you can use its installed package and browser. Otherwise, install Playwright Test in the project and install its browser binaries:
npm init playwright@latest
npx playwright install
Save the example as a test file such as tests/angular-screenshot.spec.ts, then run it with:
npx playwright test tests/angular-screenshot.spec.ts
Start the Angular app before running the test, or configure Playwright Test’s webServer setting to start it. The example assumes the app is reachable at http://localhost:4200; change that to the route and port used in your project.
2. Navigate, verify readiness, and capture
Direct navigation
page.goto(url) waits for the page’s load event by default. That event covers dependent resources such as scripts, stylesheets, frames, and images, but an Angular page can still fetch data or render important content afterward. Define readiness as the UI state that must appear in the screenshot.
Prefer an accessible, stable locator such as a heading, a result row, or a status message:
await page.goto('http://localhost:4200/reports');
await expect(page.getByRole('heading', { name: 'Reports' })).toBeVisible();
await expect(page.getByRole('row', { name: /Quarterly revenue/ })).toBeVisible();
await page.screenshot({ path: 'reports.png' });
The specific selectors depend on your app. If a heading can appear before the data you need, assert the result row or another more precise condition as well. For locators and retryable assertions, see Playwright’s locator documentation.
Navigation caused by a click
When a user action changes the route, wait for the expected URL and then verify the destination content before capturing. waitForNavigation() is deprecated and can be racy; the Page API recommends waitForURL() for URL transitions.
await page.goto('http://localhost:4200');
await page.getByRole('link', { name: 'Reports' }).click();
await page.waitForURL('**/reports');
await expect(page.getByRole('heading', { name: 'Reports' })).toBeVisible();
await page.screenshot({ path: 'reports.png', fullPage: true });
You can also assert the route with await expect(page).toHaveURL(/\/reports$/). A URL change proves navigation reached the route; the content assertion proves the page state you need is visible.
3. Choose the capture type
Save an image file
Use page.screenshot() to save an image artifact. Without fullPage, Playwright captures the current viewport. Set fullPage: true to capture the full scrollable page:
await page.screenshot({ path: 'viewport.png' });
await page.screenshot({ path: 'full-page.png', fullPage: true });
For a full-page image, make sure the content you expect is present before capturing. Pages that load sections or images while scrolling may need a readiness condition appropriate to those sections.
Check a visual baseline
For visual regression testing, use Playwright Test’s screenshot assertion instead of writing an image file yourself:
import { test, expect } from '@playwright/test';
test('dashboard matches its visual baseline', async ({ page }) => {
await page.goto('http://localhost:4200');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
await expect(page).toHaveScreenshot('dashboard.png', { fullPage: true });
});
The screenshot assertion waits until two consecutive captures match before comparing against the expectation. Keep baseline generation and comparison in a consistent browser and operating-system setup because rendering can vary with the host OS, browser version, settings, and hardware. See visual comparisons and the Page assertions API.
4. Choose a readiness condition that matches the page
There is no universal signal that every modern web page has finished all meaningful work. Pick a condition tied to the content the image must show:
| Screenshot needs to show | Example readiness check |
|---|---|
| A particular page or route | Assert the expected heading is visible |
| Data returned by the app | Assert a result row, value, or populated list is visible |
| A completed operation | Assert its success or status message is visible |
| A destination reached through a click | Wait for the expected URL, then assert destination content |
Do not use networkidle as a universal test-readiness condition. Playwright documents it as discouraged and recommends web assertions to assess readiness. The event means there have been no network connections for at least 500 ms; that does not establish that the desired UI has rendered, and pages with ongoing network activity may never reach it. A fixed sleep can wait without proving the page is ready, too. Use it only when a real, app-specific timing requirement cannot be expressed as a UI condition. See the Page API.
5. Angular-specific guidance
For modern Angular pages, start with a visible condition for the content being captured. A generic Angular synchronization hook is not required by default. Playwright’s migration guide maps Protractor’s waitForAngular pattern to Playwright’s auto-waiting in general cases, while describing a polyfill for edge cases. That guidance does not mean every Angular app needs the legacy helper. See Migrating from Protractor.
If the app has a genuine edge case where user-visible conditions cannot establish readiness, investigate the app-specific behavior and synchronization point. Keep the screenshot tied to the actual state it must depict instead of assuming framework-wide idleness guarantees that state.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Screenshot is blank or shows a loading shell | load completed before Angular finished fetching data or rendering the target UI. |
Assert a visible locator for the required content before capturing. If needed, assert both the page heading and the data element. |
| Screenshot sometimes captures old or partial content | The readiness check only proves navigation or a shell appeared, not that the intended content is ready. | Use a more specific locator, such as the result row or final status message. Wait for the destination URL after action-driven navigation, then assert its content. |
Test times out waiting for networkidle |
The page keeps network connections open or continues background requests. | Replace the idle wait with a locator assertion for the state required in the image. |
| Test times out on a locator assertion | The selector or accessible name does not match, the page did not reach the expected route, or the content never appeared. | Check the route, locator, accessible name, and app state. Assert the element that actually signals completion in this view. |
| Click-driven navigation is missed intermittently | A deprecated or racy navigation wait is being used, or the test does not check the resulting URL. | Use page.waitForURL() or expect(page).toHaveURL(), then assert destination content before the screenshot. |
| Visual screenshot differs across machines | Browser, OS, settings, or hardware can affect rendering. | Generate and compare visual baselines in a consistent browser and operating-system environment. |
7. Performance, reliability, and cost
Use the smallest readiness condition that reliably proves the required state. A targeted locator assertion avoids waiting for unrelated network activity and gives a clear failure when the expected UI never appears. Full-page captures contain more content than viewport captures, so choose based on the artifact you need.
For repeatable visual comparisons, keep the browser and operating system consistent with the baseline environment. The cited Playwright guidance provides no universal performance figure or screenshot cost: runtime depends on your page and test environment. The practical cost of an unreliable wait is a test that either fails before the required UI appears or spends time waiting for a condition unrelated to the screenshot.
Or skip the browser setup
For a one-call capture, ScreenshotNeo is a website screenshot API and MCP server. This example returns a screenshot from a URL; see the ScreenshotNeo documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners and consent prompts are accepted before capture; known consent platforms, newsletter popups, and chat widgets are removed. Each cleanup step can be turned off.
- Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers report the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
FAQ
Does page.goto() wait for Angular to finish rendering?
It waits for the load event by default. Angular can still update the page after that, so assert the visible state the screenshot requires.
Should I use waitForAngular with Playwright?
Usually no. Playwright’s migration guidance says auto-waiting covers general cases; use an Angular-specific helper only for an investigated edge case.
Should I use page.screenshot() or toHaveScreenshot()?
Use page.screenshot() to save an image artifact. Use toHaveScreenshot() to compare the page with a visual baseline.
Why does my screenshot test pass locally but differ in CI?
Rendering can vary across operating systems, browser versions, settings, and hardware. Keep baseline creation and comparison environments consistent.


