Capture the chart element, not an arbitrarily small plotting path. Wait for the chart application’s real ready state, verify that the rendered SVG or chart wrapper has positive dimensions, and use ElementHandle.screenshot() for a chart-only image. Use fullPage: true only when the complete document is wanted. If you create a custom clip, calculate it in page CSS-pixel coordinates; raw SVG getBBox() values are SVG user units and are not automatically valid screenshot coordinates.
Choose the capture scope first
Puppeteer offers four useful capture strategies. The right one depends on what the output must contain.
| Need | Use | Important trade-off |
|---|---|---|
| Chart only | Screenshot the outer SVG or chart wrapper with ElementHandle.screenshot() |
The selector and wrapper must include labels, legend, axes and margins. |
| Entire document | page.screenshot({ fullPage: true }) |
Includes unrelated page content and does not fix an incorrectly sized SVG or an unfinished chart. |
| Exact page region | page.screenshot({ clip: { x, y, width, height } }) |
You must calculate viewport CSS-pixel coordinates and provide enough margin for visible artwork. |
| Current viewport | page.screenshot({ path: 'chart.png' }) |
Anything outside the current viewport is omitted. |
For most chart exports, target the rendered <svg> or its enclosing chart component. A plotting path or group may exclude tick labels, a legend, title, stroke width and markers.
Prepare a deterministic Puppeteer page
Set the viewport before the chart lays itself out. A responsive chart can have different dimensions if you resize it after rendering.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
- CRISP CLARITY: This 23.8″ Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
- WORK SEAMLESSLY: This sleek monitor is virtually bezel-free on three sides, so the screen looks even bigger for the viewer. This minimalistic design also allows for seamless multi-monitor setups that enhance your workflow and boost productivity
- A BETTER READING EXPERIENCE: For busy office workers, EasyRead mode provides a more paper-like experience for when viewing lengthy documents
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto('https://example.com/dashboard', { waitUntil: 'domcontentloaded' });
domcontentloaded only says that the initial document was parsed. It is not a universal chart-ready signal. Wait for the application’s own completion condition: an event, a state attribute, a “data loaded” selector, or another condition that means data, labels and animations are finished.
Wait for rendering and dimensions
await page.waitForSelector('#chart svg');
await page.waitForFunction(() => {
const svg = document.querySelector('#chart svg');
return svg &&
svg.getBoundingClientRect().width > 0 &&
svg.getBoundingClientRect().height > 0 &&
svg.dataset.rendered === 'true'; // replace with the app's real ready signal
}, { timeout: 30000 });
Replace the example data attribute with a signal your chart code actually sets. A fixed delay can be useful as a last resort for an animation, but it is not a reliable, universal readiness test.
Capture the complete chart as an element
Once the outer target is ready, an element screenshot avoids page-coordinate arithmetic and scrolls the target into view when necessary.
const chart = await page.waitForSelector('#chart svg');
if (!chart) throw new Error('Chart SVG was not found');
await chart.screenshot({ path: 'chart.png', type: 'png' });
await browser.close();
PNG is the safest choice for fine axis text and labels. Puppeteer’s PNG output is lossless; a quality setting does not apply to PNG. If the SVG is only the plot and the legend is an HTML sibling, select the outer container instead:
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallconst chartRegion = await page.waitForSelector('#chart');
await chartRegion.screenshot({ path: 'complete-chart.png', type: 'png' });
Inspect all four edges of the resulting image. The correct target is the smallest stable element that contains every required visual part—not necessarily the element with the most obvious svg tag.
Rank #2
- CRISP CLARITY: This 22 inch class (21.5″ viewable) Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- 100HZ FAST REFRESH RATE: 100Hz brings your favorite movies and video games to life. Stream, binge, and play effortlessly
- SMOOTH ACTION WITH ADAPTIVE-SYNC: Adaptive-Sync technology ensures fluid action sequences and rapid response time. Every frame will be rendered smoothly with crystal clarity and without stutter
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
Use full-page screenshots only for a whole document
await page.screenshot({ path: 'dashboard.png', fullPage: true, type: 'png' });
fullPage: true captures the full page/document dimensions. It does not discover additional chart data, wait for lazy chart content, or repair an SVG whose CSS size or viewBox is wrong. It can also include headers, cards and unrelated controls. Use it when those surrounding elements are part of the requested image; otherwise use an element screenshot.
Build a deliberate clip when you need exact placement
A clip is useful when the desired output is a page region that is larger or smaller than any convenient element. Measure a rendered element with getBoundingClientRect(), whose values are relative to the browser viewport and expressed in CSS pixels.
const rect = await page.$eval('#chart', el => {
const r = el.getBoundingClientRect();
const margin = 12;
return {
x: Math.max(0, r.left - margin),
y: Math.max(0, r.top - margin),
width: r.width + margin * 2,
height: r.height + margin * 2
};
});
if (rect.width <= 0 || rect.height <= 0) throw new Error('Chart has no rendered size');
await page.screenshot({ path: 'chart-clip.png', clip: rect, type: 'png' });
The margin is intentional: strokes, markers and labels can extend beyond an inner plot box. If the element is partly outside the viewport, scroll it into view first or choose an element screenshot. A clip’s coordinates must be page viewport coordinates, not SVG coordinates.
Recommended Free Tools
Understand SVG geometry before measuring
getBBox() is user-space geometry
SVGGraphicsElement.getBBox() returns the smallest rectangle containing an SVG element in that element’s user coordinate system. Its default result describes the shape and can omit stroke and markers. Options can request additional visual effects where supported. Crucially, transforms applied to the element or its ancestors are not included in the returned x, y, width and height.
const box = await page.$eval('#chart svg path.series', el => {
const b = el.getBBox();
return { x: b.x, y: b.y, width: b.width, height: b.height };
});
Those numbers are not safe to pass directly to clip. A viewBox, CSS scaling, nested SVG viewport or ancestor transform can change their position and size on the page.
Rank #3
- Clear visuals. Fluid motion: A 144Hz refresh rate and 1ms MPRT deliver smooth, tear‑free motion across work, gaming, and streaming for clearer, more fluid viewing.
- Eye comfort: TÜV Rheinland 3‑star* certification reduces harmful blue light while preserving stunning color quality without compromise. *TÜV Rheinland 3-star eye comfort certification.
- Wide viewing angle: Get consistent views across a wide 178° /178° viewing angle.
- In-Plane Switching (IPS): See excellent color accuracy and consistency across wide viewing angles with In-plane Switching (IPS) technology.
- Ultra-thin bezels: Maximize your viewing experience with thin bezels.
getBoundingClientRect() is viewport CSS geometry
getBoundingClientRect() reports the rendered border-box rectangle relative to the browser viewport. It is the straightforward measurement for a page screenshot clip:
const cssRect = await page.$eval('#chart svg', el => {
const r = el.getBoundingClientRect();
return { x: r.left, y: r.top, width: r.width, height: r.height };
});
Use the outer wrapper if labels or an HTML legend sit outside the SVG root. A visible chart can therefore be larger than either a path’s getBBox() or the SVG’s tight graphics bounds.
When custom SVG conversion is unavoidable
getScreenCTM() exposes the matrix that maps an SVG element’s coordinate system into its SVG viewport. It can help convert user-space points to rendered coordinates before you combine them with a page clip. Nested SVG viewports and the SVG’s position on the page still have to be handled. In practice, selecting the rendered SVG or wrapper is safer than reimplementing this conversion.
A complete reusable capture function
async function captureSvgChart(page, {
selector = '#chart',
readySelector,
output = 'chart.png'
} = {}) {
if (readySelector) await page.waitForSelector(readySelector, { timeout: 30000 });
const handle = await page.waitForSelector(selector, { visible: true, timeout: 30000 });
if (!handle) throw new Error(`Missing chart target: ${selector}`);
await page.waitForFunction(sel => {
const el = document.querySelector(sel);
if (!el) return false;
const r = el.getBoundingClientRect();
return r.width > 0 && r.height > 0;
}, {}, selector);
await handle.screenshot({ path: output, type: 'png' });
}
Call this only after adapting selector and the readiness condition to your chart application. The function deliberately checks rendered dimensions rather than assuming that a present DOM node contains visible artwork.
Troubleshoot clipped, blank or oversized captures
Only part of the chart appears
- Check whether you captured the visible viewport, a plot path, the SVG, or the outer wrapper.
- Measure the selected target with
getBoundingClientRect()and compare its edges with the image. - If the legend, title or labels are siblings, capture their common container.
Labels, strokes or markers are cut off
A path or group’s default getBBox() can omit stroke and markers. Axis labels may be outside that group entirely. Select the chart wrapper or enlarge a deliberate clip with a margin.
Rank #4
- CURVED FOR ENHANCED ENGAGEMENT: An immersive viewing experience with a curved monitor that wraps more closely around your field of vision; It creates a wider view, enhancing depth perception and minimizing peripheral distraction
- SMOOTH PERFORMANCE FOR SEAMLESS CONTENT: Stay in the action when playing games, watching videos, or working on creative projects; The 100Hz refresh rate reduces lag and motion blur so you don't miss a thing in fast-paced moments¹
- MORE GAMING POWER: Gain the edge with optimizable game settings; Color and image contrast can be adjusted to see scenes more vividly and spot enemies hiding in the dark; Game Mode adjusts any game to fill the screen so you can view every detail²
- KEEP IT EASY ON THE EYES: Care for your eyes and stay comfortable, even during long sessions; Advanced eye comfort technology certified by TÜV reduces eye strain by minimizing blue light and reducing irritating screen flicker²
- INCREASED VERSATILITY: Connect to more; Plug devices straight into your monitor for increased flexibility, making your computing environment even more convenient
The clip is offset or scaled
Do not copy getBBox() coordinates into clip. Confirm whether every value came from SVG user units or viewport CSS pixels. For custom SVG geometry, use the transformation matrix rather than raw user-space values.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →The chart is blank or stale
Wait for the application’s own data/render completion signal, then verify nonzero dimensions. A navigation event or arbitrary timeout does not prove that asynchronous data, fonts or animation has completed.
The image contains too much page
Replace fullPage: true with an element screenshot or a measured clip. If the replacement crops labels, move outward to the chart’s containing wrapper.
The SVG has zero dimensions
Check hidden tabs, collapsed containers, missing CSS, failed data requests and responsive layout timing. Set the viewport before navigation, make the chart visible, and wait until its layout has a positive width and height.
Performance, reliability and output choices
- Reuse a browser: For batches, keep one browser process and create or reset pages rather than launching Chromium for every chart.
- Control layout: Fix viewport width, height and device scale when pixel comparisons or reproducible exports matter.
- Prefer the narrowest correct target: Element screenshots avoid rasterizing unrelated page content; full-page captures are appropriate only when the document itself is the deliverable.
- Use PNG for legibility: Lossless output preserves small labels. Choose another format only when its size or downstream requirements justify the trade-off.
- Validate the artifact: Check that the file exists, has nonzero dimensions and visibly includes axes, labels, legend and marks. A successful Puppeteer call alone does not establish that the chart is complete.
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. One request can render a URL as PNG, JPEG, WebP or PDF, while its capture flow accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets before the shot; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response reports the result in X-Page-Verdict and X-Billed headers.
For a public chart page, the simplest call is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for selectors, full-page capture, custom CSS and JavaScript, waiting for a selector or network idle, viewport and device presets, retina scale, hidden selectors, request blocking, headers, cookies, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous jobs, webhooks, bulk capture and PDF options. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
Best Value
- 【INTEGRATED SPEAKERS】Whether you're at work or in the midst of an intense gaming session, our built-in speakers provide rich and seamless audio, all while keeping your desk clutter-free.
- 【EASY ON THE EYES】 Protect your eyes and enhance your comfort with Blue-Light Shift technology. This feature reduces harmful blue light emissions from your screen, helping to alleviate eye strain during long hours of use and promoting healthier viewing habits.
- 【WIDEN YOUR PERSPECTIVE】Our sleek minimal bezel design ensures undivided attention. The nearly bezel-free display seamlessly connects in a dual monitor arrangement, delivering an unobstructed view that lets you focus on more at once, completely distraction-free.
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)
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 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan. Create a free ScreenshotNeo account.
Frequently Asked Questions
Should I screenshot the SVG or its parent div?
Use the smallest stable target that contains every required visual element. If labels, titles or legends are outside the SVG, use their common chart wrapper.
Can fullPage make a clipped SVG complete?
No. It captures the document’s full page, but it does not correct SVG sizing, coordinate mistakes or unfinished chart rendering.
Why does getBBox() not match the screenshot position?
getBBox() uses SVG user coordinates and excludes ancestor transforms. Screenshot clips use page viewport CSS pixels; use getBoundingClientRect() or perform an explicit matrix conversion.
What should I wait for before capturing?
Wait for the chart application’s own data/render completion signal and verify that the selected target has positive rendered width and height.

