How to Capture a Full-Height Screenshot with an Exact Width
Set an exact CSS width, capture the entire document height, and avoid common viewport, DPR, lazy-loading, and sticky-header problems.
To capture a full-height screenshot at an exact width, set the browser viewport to the required width in CSS pixels, then use a full-page capture command. Do not set a fixed height unless you want only the visible viewport.
Choose the right width and height model
Browser width controls describe CSS pixels. A 1200 px viewport means the page lays itself out as 1200 CSS pixels wide. The exported image can be wider in physical pixels when device-pixel-ratio (DPR) or retina scaling is enabled.
| Goal | Width setting | Height setting | Result |
|---|---|---|---|
| Whole document at an exact layout width | Required CSS width | Omit fixed height | Full page from top to bottom |
| Only the visible viewport | Required CSS width | Set viewport height | Viewport-sized image |
| Exact output bitmap width | Set CSS width, then account for DPR | Depends on capture mode | Output width equals CSS width × DPR |
For example, a 1200 CSS-pixel viewport captured at DPR 2 can produce an image about 2400 pixels wide. Keep DPR at 1 when “1200 px wide” refers to CSS layout width.
Chrome DevTools: exact width with a full-page capture
- Open the page in Chrome.
- Open DevTools and toggle Device Mode.
- Choose Responsive.
- Enter the required width, such as
1200. Set any convenient height; it will not limit a full-size capture. - Open the DevTools capture menu and choose Capture a full size screenshot.
Chrome distinguishes the ordinary viewport screenshot from the full-size command. The latter includes content outside the visible viewport. See the Chrome DevTools screenshot documentation.
Check the result
- Open image properties and confirm the width is what you expect for the selected DPR.
- Look for repeated sticky headers or floating buttons.
- Scroll the live page once before capture if images or sections load only after scrolling.
Firefox: GUI and repeatable command-line capture
Developer Tools button
Firefox can capture an entire page or a single element. If the screenshot button is missing, open Developer Tools settings and enable the Take a screenshot of the entire page toolbox button, then click the screenshot icon. The Firefox screenshot documentation covers this workflow.
Web Console helper
For repeatable captures, use the Web Console:
:screenshot page-1200.png --fullpage
Useful options include:
--filenameor a filename argument to control output naming.--dpr 1to keep output density aligned with CSS pixels; use a higher value for a denser bitmap.--delay 1000to wait for late content, in milliseconds.--selector .articleto capture one element instead of the full document.
Keep the viewport width set to the required value before running the command. DPR changes output pixel density, not the page’s CSS layout width.
shot-scraper: scriptable exact-width captures
shot-scraper exposes width and height directly. Omitting --height requests the full page length:
shot-scraper https://example.com/ --width 1200 -o example-1200.png
This sets a 1200 CSS-pixel browser width and lets the tool determine the document’s full height. Add --height only for a fixed viewport screenshot:
shot-scraper https://example.com/ --width 1200 --height 800 -o viewport-1200x800.png
Options that matter for dynamic pages
--waitpauses before capture.--wait-forwaits for a selector to appear.--retinauses a device scale factor of 2, so the output bitmap can be twice as wide as the CSS viewport.- Selector capture and JavaScript hooks help reveal content that is hidden until interaction.
Automating a full-height screenshot in code
Any browser automation library follows the same sequence: create a context with the exact viewport width, wait for the page to settle, then request a full-page screenshot. The following Playwright example is runnable after installing Playwright with npm install playwright.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1200, height: 800 },
deviceScaleFactor: 1
});
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'example-1200-full.png', fullPage: true });
await browser.close();
})();
The height of 800 only defines the initial viewport. fullPage: true determines the final document height. For pages with late image loading, wait for a known selector or add a short delay before the screenshot.
Dynamic content, lazy images, and fixed elements
- Lazy-loaded images: scroll through the page or wait for the image elements to finish loading before capture.
- Fonts: wait for
document.fonts.readyin automation to avoid layout shifts. - Cookie banners and chat widgets: dismiss or hide them before capture if they obscure content.
- Sticky headers: a full-page stitch can repeat a fixed header. Inspect the output and hide the header temporarily when a clean document is required.
- Animations: pause animations or wait for a stable state so sections do not appear at different positions.
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.evaluate(async () => {
document.querySelectorAll('*').forEach(el => {
el.style.animation = 'none';
el.style.transition = 'none';
});
await document.fonts.ready;
});
await page.screenshot({ path: 'stable.png', fullPage: true });
Exact CSS width versus exact image width
Decide which measurement your downstream system requires:
- Layout width: set the viewport to the target CSS width and use DPR 1.
- Retina output: keep the same CSS width and raise DPR; the bitmap becomes denser.
- Fixed bitmap width: divide the desired bitmap width by DPR, then use that CSS viewport width. Verify the resulting file because browser tooling can round dimensions.
Do not infer CSS width from a file’s pixel dimensions without knowing its DPR.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Image is only viewport height | A fixed height or ordinary viewport capture was used | Remove the height option and enable full-page/full-size capture. |
| Output is twice as wide | Retina or DPR 2 is enabled | Use DPR 1, or account for the scale when specifying CSS width. |
| Bottom sections are blank | Lazy loading has not triggered | Scroll before capture, wait for a selector, or add a delay. |
| Fonts or cards move between runs | Web fonts or animations are still loading | Wait for fonts, disable animations, and capture after network activity settles. |
| Cookie notice covers the page | Consent UI was not dismissed | Accept or reject the banner, or hide its selector before capture. |
| Sticky header repeats down the image | Fixed positioning is preserved during full-page stitching | Hide the fixed element temporarily or use a capture method that handles sticky elements for your page. |
| Very tall page fails or is truncated | Browser or image memory limits | Capture sections or use a PDF/stitched workflow; avoid assuming a universal maximum height. |
Performance, reliability, and cost
- Full-page captures require more memory than viewport shots because the browser renders and encodes a taller image.
- Waiting for network idle improves consistency but increases latency on pages with long-lived requests.
- Use a fixed viewport width, DPR, user agent, and wait policy when comparing screenshots over time.
- Cache stable pages when your workflow permits it, and split extremely long documents when image memory becomes a problem.
- Browser-based workflows have no required purchase, but you operate the browser, dependencies, fonts, and failure handling yourself.
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. Set the viewport width and enable full-page capture in the request; the API handles browser setup and returns PNG, JPEG, WebP, or PDF. The ScreenshotNeo docs list the width, full-page, wait, device, and rendering options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -d width=1200 -d full_page=true -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com", "width": 1200, "full_page": "true"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com',
width: '1200',
full_page: 'true'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server gives Claude, Cursor, and other MCP clients take_screenshot, get_page_info, and capture_pdf tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Create a free ScreenshotNeo account and start with 1,000 screenshots per month at no cost.
FAQ
Does full-page mean unlimited height?
No. The browser still has memory and image-encoding limits. For exceptionally long pages, capture sections or produce a PDF.
Should I use 1200 CSS pixels or 1200 output pixels?
Use a 1200 CSS-pixel viewport for layout fidelity. Use DPR 1 when the saved bitmap must also be about 1200 pixels wide.
Why does adding height change the result?
A height option defines a fixed viewport. Omit it when you want the capture tool to determine the document’s full height.
Can I capture one section at the same width?
Yes. Use Firefox’s selector option, shot-scraper’s selector features, browser automation locators, or ScreenshotNeo’s element capture option.


