How to Take Full Page Screenshots with Playwright in Python

Learn how to capture full page screenshots with Playwright in Python. Master the full_page parameter, handle infinite scroll pages, lazy-loaded images, and maximum size limits.

Blog post 4 min read

Written by

Dmytro Krasun

Published on

The full_page=True parameter in Playwright is one of its best features. Unlike Selenium, which requires complicated scrolling and stitching, Playwright handles it natively. But there are nuances you need to understand.

The Basics

Here’s the simplest full page screenshot:

from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('https://example.com')
page.screenshot(path='fullpage.png', full_page=True)
browser.close()

That’s it for static pages. But most modern websites aren’t static.

Understanding Viewport vs Full Page

It’s important to understand the difference:

  • Viewport: The visible area of the browser window (e.g., 1920x1080)
  • Full Page: The entire scrollable content (could be 1920x5000 or more)
# Viewport only (default)
page.screenshot(path='viewport.png')
# Full scrollable page
page.screenshot(path='fullpage.png', full_page=True)

The viewport setting still matters for full page screenshots—it determines the width:

context = browser.new_context(
viewport={'width': 1440, 'height': 900}
)
page = context.new_page()
page.goto('https://example.com')
# Screenshot will be 1440px wide, but full height
page.screenshot(path='fullpage.png', full_page=True)

Handling Lazy-Loaded Images

Modern websites lazy-load images to improve performance. If you take a screenshot immediately, you’ll see placeholder images or blank spaces.

Solution: Scroll First, Screenshot Later

from playwright.sync_api import sync_playwright
def scroll_page(page):
"""Scroll through the page to trigger lazy loading."""
page.evaluate('''
async () => {
await new Promise((resolve) => {
let totalHeight = 0;
const distance = 300;
const timer = setInterval(() => {
const scrollHeight = document.body.scrollHeight;
window.scrollBy(0, distance);
totalHeight += distance;
if (totalHeight >= scrollHeight) {
clearInterval(timer);
window.scrollTo(0, 0); // Scroll back to top
resolve();
}
}, 100);
});
}
''')
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('https://example.com')
# Scroll to trigger lazy loading
scroll_page(page)
# Wait for images to load
page.wait_for_load_state('networkidle')
# Now take the screenshot
page.screenshot(path='fullpage.png', full_page=True)
browser.close()

Handling Infinite Scroll Pages

Some pages load more content as you scroll (like social media feeds). Here’s how to handle them:

from playwright.sync_api import sync_playwright
def scroll_infinite_page(page, max_scrolls=10):
"""Scroll an infinite page until no new content loads."""
prev_height = -1
scroll_count = 0
while scroll_count < max_scrolls:
# Scroll to bottom
page.evaluate('window.scrollTo(0, document.body.scrollHeight)')
page.wait_for_timeout(1000) # Wait for content to load
# Check if new content was loaded
new_height = page.evaluate('document.body.scrollHeight')
if new_height == prev_height:
break # No new content
prev_height = new_height
scroll_count += 1
# Scroll back to top
page.evaluate('window.scrollTo(0, 0)')
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto('https://news.ycombinator.com')
scroll_infinite_page(page, max_scrolls=5)
page.wait_for_timeout(500)
page.screenshot(path='fullpage.png', full_page=True)
browser.close()

Fixed Headers and Footers

Fixed (sticky) elements can cause issues in full page screenshots—they appear multiple times as the page renders. Here’s how to handle them:

page.goto('https://example.com')
# Hide fixed elements before screenshot
page.add_style_tag(content='''
* {
position: static !important;
}
.sticky-header,
.fixed-footer,
[style*="position: fixed"],
[style*="position: sticky"] {
position: relative !important;
}
''')
page.screenshot(path='fullpage.png', full_page=True)

Or more targeted:

# Hide specific fixed elements
page.evaluate('''
document.querySelectorAll('.sticky-header, .fixed-nav').forEach(el => {
el.style.position = 'relative';
});
''')

Maximum Size Considerations

Very long pages can cause issues:

  1. Memory: Large screenshots consume significant memory
  2. File Size: PNG files can become huge
  3. Rendering: Some systems limit image dimensions

Solution: Use JPEG for Large Screenshots

page.screenshot(
path='fullpage.jpg',
full_page=True,
type='jpeg',
quality=85
)

Solution: Capture in Sections

For extremely long pages, capture in sections:

from playwright.sync_api import sync_playwright
from PIL import Image
import io
def capture_in_sections(page, section_height=2000):
"""Capture a long page in sections and stitch together."""
total_height = page.evaluate('document.body.scrollHeight')
viewport_width = page.evaluate('window.innerWidth')
images = []
offset = 0
while offset < total_height:
# Scroll to position
page.evaluate(f'window.scrollTo(0, {offset})')
page.wait_for_timeout(100)
# Capture viewport
screenshot_bytes = page.screenshot()
images.append(Image.open(io.BytesIO(screenshot_bytes)))
offset += section_height
# Stitch images together
total_height = sum(img.height for img in images)
result = Image.new('RGB', (viewport_width, total_height))
y_offset = 0
for img in images:
result.paste(img, (0, y_offset))
y_offset += img.height
return result

Async Version for Better Performance

import asyncio
from playwright.async_api import async_playwright
async def full_page_screenshot(url, output_path):
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto(url)
# Scroll to load lazy content
await page.evaluate('''
async () => {
await new Promise(resolve => {
let totalHeight = 0;
const distance = 300;
const timer = setInterval(() => {
window.scrollBy(0, distance);
totalHeight += distance;
if (totalHeight >= document.body.scrollHeight) {
clearInterval(timer);
window.scrollTo(0, 0);
resolve();
}
}, 100);
});
}
''')
await page.wait_for_load_state('networkidle')
await page.screenshot(path=output_path, full_page=True)
await browser.close()
asyncio.run(full_page_screenshot('https://example.com', 'fullpage.png'))

Comparison: Playwright vs Selenium Full Page

FeaturePlaywrightSelenium
Native full pagefull_page=TrueNot supported
Workaround neededNoYes (scroll + stitch)
QualityExcellentDepends on implementation
SpeedFastSlow

This is one of the main reasons I recommend Playwright over Selenium for screenshot tasks.

When Screenshots Aren’t Enough

For production systems handling thousands of screenshots, consider:

  • Memory management becomes critical
  • Browser crashes can lose progress
  • Rate limiting and retries add complexity

A screenshot API like ScreenshotOne handles these challenges. See the main Python screenshot guide for when to use each approach.

Summary

Full page screenshots with Playwright are straightforward, but remember:

  1. Use full_page=True for complete captures
  2. Scroll first to trigger lazy-loaded content
  3. Handle infinite scroll with max scroll limits
  4. Remove fixed positioning for clean results
  5. Use JPEG for very long pages to manage file size

Frequently Asked Questions

If you read the article, but still have questions. Please, check the most frequently asked. And if you still have questions, feel free reach out at support@screenshotone.com.

How to take full page screenshot with Playwright Python?

Use page.screenshot(path='screenshot.png', full_page=True). The full_page parameter captures the entire scrollable page height, not just the visible viewport.

What is the maximum screenshot size in Playwright?

Playwright doesn't have a hard limit, but very long pages (over 16,384 pixels) may cause memory issues. For extremely long pages, consider capturing in sections or using a screenshot API.

How to handle lazy-loaded images in full page screenshots?

Scroll through the page first to trigger lazy loading, then wait for images to load before taking the screenshot. Use page.evaluate() to scroll and page.wait_for_load_state('networkidle') to wait.

Read more Screenshot rendering

Interviews, tips, guides, industry best practices, and news.

View all posts

Automate website screenshots

Exhaustive documentation, ready SDKs, no-code tools, and other automation to help you render website screenshots and outsource all the boring work related to that to us.