Screenshot Webpages as JPEG
Learn how to capture a full webpage as a JPEG with Playwright, Firefox, Puppeteer, shot-scraper, and ScreenshotNeo.
Yes. For a repeatable full-page JPEG, use Playwright and set fullPage: true with type: 'jpeg'. Set quality explicitly from 0 to 100. A viewport capture records only the visible area; a full-page capture records the entire scrollable document.
Fastest repeatable method: Playwright
Install Playwright, launch a browser, open the page, and save the screenshot. The example below captures the complete scrollable page as a JPEG at quality 85.
npm install playwright
npx playwright install chromium
const { chromium } = require('playwright');
(async () => {
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.jpg',
type: 'jpeg',
quality: 85,
fullPage: true
});
await browser.close();
})();
Playwright documents JPEG output, a quality range of 0–100 (default 80), full-page capture, and CSS-pixel versus device-pixel scaling in its Page screenshot API and screenshot guide.
Choose the capture scope
| Goal | Setting | Result |
|---|---|---|
| What a visitor currently sees | omit fullPage or set it to false |
Viewport screenshot |
| Entire scrollable document | fullPage: true |
One tall JPEG containing the page |
| One component | locator.screenshot() |
JPEG cropped to the selected element |
await page.locator('.pricing-table').screenshot({
path: 'pricing.jpg',
type: 'jpeg',
quality: 90
});
Element capture is useful for cards, charts, and product sections. Check that the selector identifies a visible element before capturing it.
JPEG quality and image scale
- Quality: use 70–80 for smaller previews, 85–95 for documentation or review images, and 100 when you need the least JPEG compression. JPEG is lossy, so sharp text and thin lines can show artifacts at low values.
- CSS scale: one image pixel corresponds to one CSS pixel.
- Device scale: one image pixel corresponds to one device pixel. A device scale factor above 1 increases dimensions and file size.
const context = await browser.newContext({
viewport: { width: 1280, height: 800 },
deviceScaleFactor: 1
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'page.jpg', type: 'jpeg', quality: 88, fullPage: true });
Use a fixed viewport and device scale when you need comparable images between runs. Larger scale factors produce sharper output on high-density displays but require more memory and storage.
Wait for dynamic content before capture
A screenshot can be correct syntactically while still showing loading placeholders, collapsed sections, or late web fonts. Navigate with an appropriate wait condition, then wait for a known selector or a short delay when the page requires it.
await page.goto('https://example.com/dashboard', { waitUntil: 'domcontentloaded' });
await page.locator('[data-ready="true"]').waitFor({ state: 'visible', timeout: 15000 });
await page.screenshot({ path: 'dashboard.jpg', type: 'jpeg', quality: 85, fullPage: true });
Prefer a page-specific readiness selector over an arbitrary long sleep. If the page never becomes idle because of analytics or streaming requests, use domcontentloaded plus a readiness check.
Manual capture in Firefox
Firefox Developer Tools can capture a full page or an individual element from its screenshot controls. Mozilla’s instructions describe a PNG-style default filename, so this route is suitable when PNG meets your requirement; use a scripted tool when you need a controlled JPEG quality setting or recurring captures. See Mozilla’s screenshot documentation.
- Open the page in Firefox.
- Open Developer Tools and choose the screenshot command.
- Select the full-page or element option.
- Save the generated image, then convert it to JPEG with an image tool if required.
Other automation options
Puppeteer
Puppeteer exposes a programmable Page.screenshot() method. Check the current ScreenshotOptions documentation for the format and scope options supported by your installed version.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900 });
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
await page.screenshot({
path: 'page.jpg',
type: 'jpeg',
quality: 85,
fullPage: true
});
await browser.close();
})();
shot-scraper
shot-scraper provides a command-line workflow with browser selection and JPEG quality controls. This is convenient for shell scripts and scheduled jobs; pin the version and review its current options before deploying.
shot-scraper install
shot-scraper https://example.com page.jpg --quality 85 --browser chromium
Python Playwright example
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 1440, "height": 900})
page.goto("https://example.com", wait_until="networkidle")
page.screenshot(path="page.jpg", type="jpeg", quality=85, full_page=True)
browser.close()
pip install playwright
playwright install chromium
Or skip the browser setup
ScreenshotNeo provides a GET endpoint for website screenshots and PDFs. The request below returns a WebP by default; use the API options in the documentation when you need a specific output or capture configuration.
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}`);
Before capture, cookie and consent banners, newsletter popups, and chat widgets are removed. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers identify the page verdict and billing result. ScreenshotNeo also has an MCP server so Claude, Cursor, and other MCP clients can call take_screenshot, get_page_info, and capture_pdf. One thousand screenshots per month are free with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Useful capture options to plan for
| Requirement | Approach |
|---|---|
| Lazy-loaded images | Use full-page capture and wait for the page’s images or ready marker. |
| Dark mode | Set the browser color scheme before navigation. |
| Authenticated pages | Use a controlled browser context with the required cookies or headers. |
| Stable layout | Fix viewport, scale, fonts, and readiness conditions. |
| Large batches | Reuse a browser process, limit concurrency, and write files incrementally. |
| JPEG versus PNG/WebP | Choose JPEG for smaller photographic pages; choose a lossless or modern format when text edges or transparency matter. |
Troubleshooting
The image contains only the visible viewport
Set fullPage: true. Confirm that you are passing the option to page.screenshot(), not only to the browser context.
The JPEG is blurry or has halos around text
Increase quality, use a device scale factor of 1 or higher, and avoid repeatedly converting the same JPEG.
Content is missing at the bottom
Wait for lazy-loaded content and verify that the page’s scroll height has settled. A readiness selector is more reliable than a fixed delay.
The script hangs while waiting for network idle
Long-lived analytics, advertisements, or sockets can prevent network idle. Use domcontentloaded, then wait for a specific application-ready selector.
Fonts or icons differ between runs
Run captures in the same browser version and environment, wait for fonts to load, and keep viewport and device scale fixed.
An element screenshot fails
Check the selector, wait for the element to be visible, and ensure it is not covered by a modal or outside the viewport.
The output file is unexpectedly large
Lower JPEG quality, reduce device scale, capture an element instead of the whole page, or use a format and dimensions suited to the destination.
Performance, reliability, and cost considerations
- Launching a browser for every URL adds startup time. Reuse one browser and create separate pages or contexts for a batch.
- Full-page captures consume more memory as page height and device scale increase. Process very tall pages in controlled batches.
- Use explicit timeouts and close pages in a
finallyblock so failed jobs do not leave browsers running. - Cache inputs such as static pages when your workflow permits it, but invalidate the cache when visual freshness matters.
- For a hosted workflow, ScreenshotNeo bills only clean shots; failed loads, bot checks, blank pages, timeouts, and cache hits are free, and the response headers report the result.
FAQ
Can I save an entire website as one JPEG?
You can save one webpage’s full scrollable document as a JPEG. A multi-page site requires a separate capture for each URL.
What quality should I use?
Start at 85 and adjust after checking text and file size. Make the value explicit so recurring captures are consistent.
Does full-page mean the browser scrolls visibly?
Playwright’s full-page option captures the scrollable document in one operation; it is not limited to the current viewport.
Is JPEG suitable for transparent backgrounds?
No. JPEG does not preserve transparency. Use a format that supports an alpha channel when transparency is required.
Which tool should I use for scheduled captures?
Use Playwright, Puppeteer, or shot-scraper when you control the runtime. Use ScreenshotNeo when you want an HTTP request, billing verdicts, consent cleanup, and MCP access without managing a browser.


