ScreenshotNeo

BlogHow-to

How to Use Selenium to Take a Website Screenshot at a Mobile Viewport

Set ChromeDriver’s mobile emulation before opening a Selenium session, then capture the viewport. Includes Python, JavaScript, full-page limits, and troubleshooting.

By the ScreenshotNeo team4 October 20269 min read

To take a website screenshot at a mobile viewport with Selenium, configure ChromeDriver’s mobile emulation before creating the browser session. Then navigate to the page, wait for the content you need, and save a screenshot. You can choose a ChromeDriver device preset or provide custom width, height, and pixel ratio. The example below captures the visible viewport; full-page capture is a separate requirement.

Mobile emulation approximates mobile rendering. It does not replace checking behavior on a physical device.

1. Choose a viewport and capture scope

Decide what the screenshot needs to represent before starting the browser:

  • Device profile: Use a recognized ChromeDriver device name when you want a preset. The available names depend on your Chrome and ChromeDriver versions; an unknown name causes an error.
  • Custom metrics: Set width, height, and pixel ratio when you need explicit dimensions or a size absent from the installed device list.
  • Orientation: Use portrait or landscape dimensions as appropriate. For responsive checks, capture more than one representative width rather than assuming one size covers all breakpoints.
  • Viewport or full page: A regular screenshot captures the currently visible viewport. A full-size capture includes content outside it; support and method vary by browser and WebDriver implementation.

ChromeDriver documentation gives 360 × 640 at pixel ratio 3.0 and 412 × 823 at pixel ratio 1.75 as configuration examples. These are examples, not universal defaults. Pick dimensions that match the page or responsive breakpoint you need to inspect. ChromeDriver mobile emulation documentation describes both named profiles and custom metrics.

2. Install Selenium and configure ChromeDriver

Use a Selenium language binding and a compatible Chrome/ChromeDriver setup. Mobile emulation must be added to browser options before the driver session starts.

Python: custom mobile viewport

Install Selenium with python -m pip install selenium. Save the following as mobile_screenshot.py and run it with python mobile_screenshot.py. It uses Selenium Manager to obtain or locate a compatible driver where supported by the installed Selenium version and environment.

from pathlib import Path
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait

url = "https://example.com"
output = Path("mobile-viewport.png")

options = Options()
options.add_experimental_option(
    "mobileEmulation",
    {
        "deviceMetrics": {
            "width": 390,
            "height": 844,
            "pixelRatio": 3.0,
        }
    },
)

# Set headless mode if this machine has no graphical display.
# options.add_argument("--headless=new")

driver = webdriver.Chrome(options=options)
try:
    driver.set_page_load_timeout(60)
    driver.get(url)

    # Replace this condition with a page-specific element when possible.
    WebDriverWait(driver, 20).until(
        lambda d: d.execute_script("return document.readyState") == "complete"
    )

    if not driver.save_screenshot(str(output)):
        raise RuntimeError("WebDriver did not save the screenshot")
    print(f"Saved {output} ({driver.get_window_size()})")
finally:
    driver.quit()

save_screenshot captures the current viewport. Waiting for document.readyState does not guarantee that client-rendered content, late images, or animations are finished. If the target page has a known main element, wait for it explicitly:

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

WebDriverWait(driver, 20).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, "main"))
)

Python: use a named device profile

Use the same setup but replace the custom metrics with a recognized device name:

options.add_experimental_option(
    "mobileEmulation",
    {"deviceName": "Pixel 2"},
)

The device name must exist in the emulated-device settings understood by the installed ChromeDriver. Presets can change across browser and driver versions. If ChromeDriver rejects the name, choose a profile available in that version or use explicit metrics.

JavaScript: custom viewport with Selenium

Install the Selenium JavaScript package with npm install selenium-webdriver. Save this as mobile-screenshot.js and run node mobile-screenshot.js. The Chromium options API exposes setMobileEmulation.

const { Builder, Browser, By, until } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');

async function main() {
  const options = new chrome.Options();
  options.setMobileEmulation({
    deviceMetrics: { width: 390, height: 844, pixelRatio: 3 },
  });
  // Uncomment on a machine without a graphical display.
  // options.addArguments('--headless=new');

  const driver = await new Builder()
    .forBrowser(Browser.CHROME)
    .setChromeOptions(options)
    .build();

  try {
    await driver.manage().setTimeouts({ pageLoad: 60000 });
    await driver.get('https://example.com');
    await driver.wait(async () => {
      return (await driver.executeScript('return document.readyState')) === 'complete';
    }, 20000);

    // Prefer a page-specific readiness condition when the site renders content
    // after the initial document load.
    // await driver.wait(until.elementIsVisible(
    //   await driver.findElement(By.css('main'))
    // ), 20000);

    const png = await driver.takeScreenshot();
    require('node:fs').writeFileSync('mobile-viewport.png', png, 'base64');
    console.log('Saved mobile-viewport.png');
  } finally {
    await driver.quit();
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

For a named profile, use options.setMobileEmulation({ deviceName: 'Pixel 2' }), with a name recognized by the ChromeDriver version in use.

3. Tune device emulation when the page needs it

ChromeDriver’s mobileEmulation setting accepts either a device name or custom device attributes. Custom metrics include:

Setting What it controls When to set it
width, height Emulated device dimensions and responsive layout signals To target a particular viewport or breakpoint
pixelRatio Device pixel ratio When screenshot density or high-DPI behavior matters
mobile, touch Mobile and touch behavior flags Usually these default to true; set explicitly when you need to document or vary the behavior
userAgent User-agent string sent by the browser When the site varies its response based on browser identity
clientHints Client hints describing browser/device identity When the page relies on client hints as well as the user-agent string

Example custom configuration with optional identity fields:

mobile_emulation = {
    "deviceMetrics": {
        "width": 412,
        "height": 823,
        "pixelRatio": 1.75,
        "mobile": True,
        "touch": True,
    },
    "userAgent": "YOUR_TARGET_USER_AGENT",
    # Add clientHints only when you have the values required by your test.
    # "clientHints": {...},
}
options.add_experimental_option("mobileEmulation", mobile_emulation)

Do not copy an arbitrary user-agent string and assume it creates a physical phone. The emulation changes device metrics and related browser behavior, including media-query results, but does not run the page on mobile hardware. Microsoft Edge Developer documentation calls device emulation “a first-order approximation of the look and feel of your page on a mobile device.” Validate hardware-dependent behavior on an actual target device. See the Edge device emulation documentation.

4. Capture a full page when needed

Selenium’s standard screenshot command captures what is currently visible. Chrome DevTools distinguishes this from a full-size screenshot, which includes content beyond the viewport. Full-page screenshot methods vary by browser and implementation, so confirm the capability of the Selenium binding and browser you have selected.

For a page where ordinary scrolling lazy-loads content, first scroll through the page and allow newly revealed content to load, then use a supported full-page capture method. A viewport screenshot after scrolling only captures the visible segment. Stitching screenshots taken at different scroll positions is possible, but sticky headers, animations, changing content, and fractional scroll offsets can create seams or duplicates. If you stitch, record scroll positions and verify the resulting image.

5. Make captures repeatable

  1. Pin the target URL and browser setup. Use compatible Chrome and ChromeDriver versions and record the chosen viewport dimensions, pixel ratio, and any identity overrides.
  2. Wait for the right state. Prefer a condition tied to meaningful content over a fixed sleep. If the site has a client-rendered chart or image, wait for that element or a page-specific signal.
  3. Control known sources of variation. Use a stable test page or account state, and avoid capturing during transitions or animation. Disable or wait for animated content only if your test permits altering page behavior.
  4. Keep scope explicit. Name files to indicate viewport or full-page capture and the selected profile or dimensions.
  5. Inspect the output. Confirm the file exists, opens, and contains the intended content. Check dimensions when downstream image comparison depends on exact size.
  6. Quit the driver in a finally block. This closes Chrome cleanly even when navigation or saving fails.

6. Troubleshooting

Symptom Likely cause Fix
ChromeDriver reports an unknown device name The profile is not present in the device list for the installed Chrome/ChromeDriver combination. Use a recognized name for that version or configure explicit deviceMetrics.
The page looks like desktop layout Mobile emulation was added after the driver started, custom metrics were malformed, or the page does not use the expected responsive breakpoint. Set emulation on the options before creating the driver; verify width and height and inspect the page’s breakpoints.
Screenshot has the wrong dimensions Viewport CSS pixels and output image pixels can differ because pixel ratio affects device rendering; window sizing and screenshot scope also matter. Check the configured metrics and resulting image dimensions. Revisit the desired viewport and pixel ratio instead of treating them as the same measurement.
Screenshot is blank or missing content Capture happened before client-side rendering or image loading completed, or navigation reached an error/interstitial page. Wait for a page-specific visible element or readiness signal, then inspect the current URL and browser state before saving.
TimeoutException during navigation The page or a resource did not finish loading before the configured page-load timeout. Increase the timeout if slow loading is expected; where suitable, use a less strict page-load strategy and explicitly wait for the content required for the screenshot.
Driver cannot start or connect Chrome is missing, versions are incompatible, or the execution environment cannot launch a graphical browser. Install a compatible browser/driver setup, check Selenium’s driver resolution output, and use headless mode in display-less environments.
Mobile-specific behavior differs from a real phone Emulation approximates device characteristics but does not reproduce all hardware, browser, sensor, or interaction behavior. Use emulation for responsive layout checks and validate device-dependent behavior on a physical target.
Full-page output is clipped or contains seams The chosen WebDriver screenshot only covers the viewport, or manual stitching crossed sticky/animated content. Use a supported full-size capture facility for the selected browser; if stitching is necessary, inspect overlaps and page state at each scroll position.

7. Performance, reliability, and cost

A Selenium screenshot starts and drives a browser, so the work includes browser startup, page navigation, rendering, waiting, and writing the image. Reusing a driver for a controlled sequence of pages can avoid repeated startup, but isolate sessions when cookies, local storage, or page state could affect results. Always close sessions so browser processes do not accumulate.

Reliability depends on a compatible browser/driver pair, stable page state, and a wait condition that matches the page. A fixed delay can waste time on fast pages and still be too short on slow ones. Full-page capture also consumes more time and memory as page length and image dimensions grow. Set navigation and wait timeouts deliberately, capture only the scope you need, and retry only transient failures rather than repeating deterministic configuration errors.

The Selenium workflow uses software and the browser environment; this research identifies no required physical purchase. Resource costs come from the machine or CI environment running Chrome and from maintaining the browser setup. A physical phone is useful for separate hardware validation but is not required for an emulated screenshot.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie/consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each of those steps can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers say which verdict applied and whether the result was billed. An MCP server exposes take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

For a mobile-sized capture, use the API’s viewport options documented at ScreenshotNeo docs. This one-call example requests a WebP screenshot of a target URL; adapt the target URL and add the documented viewport parameters for the size you need:

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -o mobile-shot.webp

Python equivalent:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
r.raise_for_status()
open("mobile-shot.webp", "wb").write(r.content)

Node.js equivalent:

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
require('node:fs').writeFileSync('mobile-shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo includes 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000, and every feature is on every plan. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Does Selenium mobile emulation prove a site works on iPhone or Android hardware?

No. It is useful for responsive layout and browser rendering checks, but hardware-specific behavior still needs validation on the target device.

Should I use a device preset or custom dimensions?

Use a preset when its profile is available and matches your target. Use custom metrics when you need explicit dimensions or a viewport not represented by the installed preset list.

Does save_screenshot capture the entire page?

It captures the current viewport. Use a browser-supported full-size capture method for content beyond the visible area.

Why can two screenshots at the same viewport width differ?

Viewport width alone does not fix page state: content, fonts, loading, browser versions, pixel ratio, and responsive breakpoints can all affect the rendered result.