Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Playwright or Puppeteer when JavaScript runs in Node.js and you need a screenshot of the page as a browser actually renders it. Use html2canvas when code runs inside the page and you want an in-page export of an element or report. A browser automation library can capture the viewport, the entire scrollable document, or one element. html2canvas is easier to add to a button in an existing page, but it reconstructs pixels from the DOM and styles rather than taking a native browser screenshot.

This guide shows how to take a screenshot of an HTML page using JavaScript, how to capture a full page or one HTML element, and how to avoid the cross-origin and timing problems that make captures blank or incomplete.

Choose the JavaScript screenshot method

Method Where it runs Best for Rendering behavior
Playwright Node.js or server-side automation High-fidelity captures, visual tests, repeatable viewport settings Drives a real browser
Puppeteer Node.js or server-side automation Chrome-oriented automation and page or element files Drives a real browser
html2canvas Browser code inside the page An “Export this card” or report button Rebuilds an image from DOM information and applied styles

For an automated service, start with Playwright. For an existing web page where the user clicks a button, start with html2canvas. Puppeteer is a solid alternative when your automation stack already uses it.

Take a full-page screenshot with Playwright

Install Playwright in your Node.js project, then create a module such as screenshot.mjs. The browser opens at a known viewport, waits for navigation and network activity to settle, and writes a PNG containing content below the fold.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'page.png', fullPage: true });
await browser.close();

fullPage: true is the important distinction from a viewport-only shot. Without it, the file represents only the visible 1440×900 area. The call also returns screenshot data if you omit path, which is useful when an API should return bytes, store an object, or pass the image to another function.

Capture one HTML element

Use a locator when the page contains a component such as an invoice, chart, or product card:

await page.locator('.invoice').screenshot({ path: 'invoice.png' });

The locator must resolve to the element you intend to export. If the selector is too broad, the resulting image may include an unexpected container; if it matches nothing, wait for the component or correct the selector before capturing.

Control the output

Playwright’s screenshot options include full-page capture, image format and quality controls, and element or locator screenshots. Use a fixed viewport for repeatable visual regression tests. Choose JPEG or WebP when file size matters; retain PNG when sharp text, transparency, or lossless output is important. If the application renders data after navigation, wait for the relevant selector or application state rather than assuming that the first loaded document is complete.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Take a full-page screenshot with Puppeteer

Puppeteer follows the same browser-driven model. Its documented pattern waits for the page to become quiet, captures the complete document, and closes the browser:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'page.png', fullPage: true });
await browser.close();

networkidle2 waits until network activity is low, which is often safer for pages that load data and images after the initial response. It is not a guarantee that every animation or application task has finished, so add an explicit wait for a selector when the page has a known ready state.

Capture an element in Puppeteer

Find the element, then call its screenshot method. The exact handle or locator code depends on your Puppeteer version and selector strategy, but the principle is the same: resolve the component after it is rendered and capture that element instead of the whole document. Puppeteer’s Page.screenshot() can return image data (a base64 string or Uint8Array, depending on options) when you do not write directly to a path.

Take a screenshot in the browser with html2canvas

html2canvas is appropriate when the JavaScript is already running on the page. It reads the current DOM and applied styles and paints a canvas; it does not ask the browser for a native screenshot. This makes it convenient for a download button, while also explaining why the result can differ from what the browser displays.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<script type="module">
  import html2canvas from 'https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/+esm';

  const element = document.querySelector('#capture');
  const canvas = await html2canvas(element, { backgroundColor: '#fff' });
  const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));

  const link = document.createElement('a');
  link.download = 'capture.png';
  link.href = URL.createObjectURL(blob);
  link.click();
  URL.revokeObjectURL(link.href);
</script>

Replace #capture with the element to export. Pass the document’s root element if you want the page area represented by the DOM, but do not expect this to reproduce browser chrome or inaccessible content. The project warns that output may not be 100% accurate to the real browser representation and that html2canvas is not a Node.js renderer.

Make the button explicit

In production, trigger the code from a user action, check that element is not null, and handle a null blob before creating the download URL. Revoke the object URL after the download has been initiated so repeated exports do not accumulate browser resources.

Full page or one element?

  • Use full-page capture when a reader needs the complete scrollable document, including sections below the fold. Set fullPage: true in Playwright or Puppeteer.
  • Use element capture for a component such as .invoice, a dashboard panel, or a report card. Element screenshots avoid unrelated navigation and page margins.
  • Use a viewport capture when you are testing exactly what a user sees at a specified width and height. Omit fullPage and set the viewport deliberately.

For visual comparisons, keep viewport dimensions, device scale, fonts, locale, and data stable. A different font load or a late animation can produce a different image even when the code is unchanged.

Timing, lazy content, and dynamic pages

Navigation completion is not the same as visual readiness. A robust capture sequence is:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Open a browser context with the desired viewport.
  2. Navigate with an appropriate wait condition such as Playwright’s networkidle or Puppeteer’s networkidle2.
  3. Wait for the selector that proves the application has rendered its data.
  4. Scroll or otherwise trigger lazy content if the page requires it.
  5. Disable or wait out animations when pixel stability matters.
  6. Capture the page or locator, then close the browser in a finally path so failures do not leak processes.

Network-idle waits can be unsuitable for pages with analytics, chat, or long-polling requests that never become quiet. In those cases, a specific ready selector or a bounded delay is more deterministic than waiting forever.

Cross-origin limitations and security

Images and fonts

html2canvas may be unable to use images or fonts fetched from another origin. The browser can taint the canvas, preventing export, unless the resource permits the required cross-origin access. Configure CORS on the asset host or serve the asset through a same-origin proxy that you control. A browser automation screenshot does not have this particular canvas restriction because it captures the rendered browser surface, although authentication and network access still need to be configured.

Cross-origin iframes

html2canvas cannot render a cross-origin iframe because browser security rules prevent access to its contentDocument. Capture the iframe’s page separately with browser automation, or arrange for the content to be available in the same origin when that is legally and technically appropriate.

Credentials and private pages

Do not place secrets in a client-side html2canvas script. For a private page, use a controlled server-side browser context with the required cookies or headers, and avoid writing sensitive screenshots to publicly readable paths.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Output formats, bytes, and storage

Playwright and Puppeteer can write PNG files or return image bytes. Returning bytes is preferable for an HTTP endpoint that streams the result or uploads it to object storage. PNG preserves crisp text and transparency; JPEG and WebP can reduce size when photographic content or bandwidth is more important. html2canvas produces a canvas, which you can convert with toBlob and then upload or download. The chosen format does not fix a rendering problem: capture only after the required content and fonts are present.

Troubleshooting checklist

Symptom Likely cause Fix
Only the top of the page appears Viewport capture was requested Set fullPage: true in Playwright or Puppeteer.
Blank or half-rendered image Capture ran before data, fonts, or images settled Wait for a ready selector, use a suitable network wait, and handle lazy content.
html2canvas throws a security or tainted-canvas error Cross-origin image or font Enable appropriate CORS or use a same-origin proxy.
An embedded frame is missing The iframe is cross-origin Capture that page separately with browser automation; html2canvas cannot read it.
Screenshot differs between runs Variable viewport, fonts, data, or animation Fix the viewport and environment, wait for fonts and a stable selector, and disable animations.
Node.js cannot use html2canvas html2canvas is a browser DOM renderer Use Playwright or Puppeteer for server-side work.
Browser process remains after an error Close was skipped Put browser cleanup in a finally block.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server, so a server can request a rendered image without managing Playwright or Puppeteer. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and whether it was billed.

One GET request returns PNG, JPEG, WebP, or PDF. The API supports full-page and CSS-selector element capture, device presets or custom viewports, dark mode, retina scale, lazy-image loading, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, TTL-based caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage data, and an OpenAPI specification. Its parameter names also match those used by other screenshot APIs, which can simplify migration.

See the ScreenshotNeo documentation for authentication and all options. A minimal cURL request is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Equivalent Python:

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)

Equivalent Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. An MCP server supplies take_screenshot, get_page_info, and capture_pdf tools to Claude, Cursor, and other MCP clients. Create a free ScreenshotNeo account to start.

Which approach should you use?

  • Choose Playwright for the most controlled, browser-faithful Node.js automation and visual regression work.
  • Choose Puppeteer when its Chrome automation model fits your existing project.
  • Choose html2canvas for a browser-only “download this element” feature when cross-origin content is under your control.
  • Choose ScreenshotNeo when you want an API or MCP workflow without maintaining browser infrastructure, especially when consent UI, failed pages, or agent-driven capture matters.

Frequently Asked Questions

Can JavaScript take a screenshot without a browser?

Not with html2canvas: it needs the page DOM and runs in a browser. For server-side JavaScript, use a real-browser automation library such as Playwright or Puppeteer, or call a screenshot API.

How do I screenshot an HTML element instead of the whole page?

In Playwright, call page.locator('your-selector').screenshot(). With html2canvas, pass the element returned by document.querySelector().

Why is my full-page screenshot still missing content?

Full-page mode extends the capture area, but it does not make an application finish rendering. Wait for the page’s data and lazy content before calling the screenshot method.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Is html2canvas the same as a native browser screenshot?

No. It reconstructs an image from DOM information and styles, so unsupported CSS, cross-origin resources, and iframes can differ from the browser’s actual pixels.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.