How to Take Mobile Screenshots of Any Website
Testing and capturing how websites look on mobile devices is a common need for developers, QA teams, and designers. Whether you're checking responsive layouts, generating mobile previews for a portfolio, or building a cross-device testing pipeline, you need a way to render web pages as they appear on specific mobile devices.
This guide covers how to capture mobile screenshots using device emulation, making a desktop browser behave exactly like an iPhone, Pixel, or iPad, with working code examples in Node.js, Python, and via API.
What Is Device Emulation?
Device emulation means configuring a headless browser to mimic a specific mobile device. This includes:
- Viewport size: The screen resolution (e.g., 393x852 for iPhone 15 Pro)
- Device scale factor (DPR): How many physical pixels per CSS pixel (e.g., 3x for iPhones)
- User agent string: Tells the website which device is requesting the page
- Touch events: Enables touch-based interactions
- Media queries: Triggers responsive CSS breakpoints
When all of these are set correctly, the website renders its mobile layout, exactly as it would on a real device.
Method 1: Puppeteer (Node.js)
Puppeteer includes built-in device descriptors for dozens of popular devices.
Using Built-In Device Presets
import puppeteer, { KnownDevices } from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Emulate iPhone 15 Pro
const device = KnownDevices['iPhone 15 Pro'];
await page.emulate(device);
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'iphone15pro.png' });
await browser.close();
Available Devices
Puppeteer includes presets for:
| Device | Viewport | DPR |
|---|---|---|
| iPhone 14 | 390x844 | 3 |
| iPhone 15 Pro | 393x852 | 3 |
| iPhone 15 Pro Max | 430x932 | 3 |
| iPhone SE | 375x667 | 2 |
| iPad Pro 11 | 834x1194 | 2 |
| Pixel 7 | 412x915 | 2.625 |
| Galaxy S21 | 360x800 | 3 |
You can list all available devices:
import { KnownDevices } from 'puppeteer';
console.log(Object.keys(KnownDevices));
Custom Device Configuration
If you need a device that's not in the presets:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({
width: 412,
height: 915,
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true
});
await page.setUserAgent(
'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36'
);
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'pixel8.png' });
await browser.close();
Landscape Mode
Swap width and height for landscape orientation:
const device = KnownDevices['iPhone 15 Pro landscape'];
await page.emulate(device);
Or manually:
await page.setViewport({
width: 852, // Height becomes width in landscape
height: 393, // Width becomes height
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
});
Method 2: Playwright (Node.js / Python)
Node.js
import { chromium, devices } from 'playwright';
const browser = await chromium.launch();
// Use built-in device preset
const context = await browser.newContext({
...devices['iPhone 15 Pro']
});
const page = await context.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'mobile.png' });
await browser.close();
Python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
# Use built-in device preset
device = p.devices["iPhone 15 Pro"]
context = browser.new_context(**device)
page = context.new_page()
page.goto("https://example.com")
page.screenshot(path="mobile.png")
browser.close()
Multiple Devices in One Script
Capture the same page across multiple devices:
from playwright.sync_api import sync_playwright
DEVICES = [
"iPhone 15 Pro",
"iPhone SE",
"Pixel 7",
"iPad Pro 11",
"Galaxy S21"
]
with sync_playwright() as p:
browser = p.chromium.launch()
for device_name in DEVICES:
device = p.devices[device_name]
context = browser.new_context(**device)
page = context.new_page()
page.goto("https://example.com", wait_until="networkidle")
filename = device_name.lower().replace(" ", "_") + ".png"
page.screenshot(path=filename)
print(f"Captured: {device_name} → {filename}")
context.close()
browser.close()
Method 3: Screenshot API
A screenshot API handles device emulation server-side. No browser installation, no device configuration, no memory management.
cURL
# iPhone 15 Pro screenshot
curl "https://app.snap-render.com/v1/screenshot?url=https://example.com&device=iphone_15_pro&format=png" \
-H "X-API-Key: YOUR_API_KEY" \
--output iphone.png
# iPad Pro screenshot
curl "https://app.snap-render.com/v1/screenshot?url=https://example.com&device=ipad_pro&format=png" \
-H "X-API-Key: YOUR_API_KEY" \
--output ipad.png
Node.js
import { SnapRender } from 'snaprender';
const client = new SnapRender('YOUR_API_KEY');
// iPhone screenshot
const iphone = await client.screenshot({
url: 'https://example.com',
device: 'iphone_15_pro',
format: 'png'
});
// Android screenshot
const pixel = await client.screenshot({
url: 'https://example.com',
device: 'pixel_7',
format: 'png'
});
// Custom viewport (no predefined device)
const custom = await client.screenshot({
url: 'https://example.com',
width: 375,
height: 812,
format: 'png'
});
Python
from snaprender import SnapRender
client = SnapRender("YOUR_API_KEY")
# iPhone screenshot
iphone = client.screenshot(
url="https://example.com",
device="iphone_15_pro",
format="png"
)
with open("iphone.png", "wb") as f:
f.write(iphone)
# Multiple devices
devices = ["iphone_15_pro", "iphone_14", "pixel_7", "ipad_pro"]
for device in devices:
image = client.screenshot(
url="https://example.com",
device=device,
format="png"
)
with open(f"{device}.png", "wb") as f:
f.write(image)
print(f"Captured: {device}")
Available Devices via API
SnapRender supports these device presets:
| Device ID | Device | Viewport | DPR |
|---|---|---|---|
iphone_14 |
iPhone 14 | 390x844 | 3 |
iphone_15_pro |
iPhone 15 Pro | 393x852 | 3 |
iphone_15_pro_max |
iPhone 15 Pro Max | 430x932 | 3 |
pixel_7 |
Pixel 7 | 412x915 | 2.625 |
ipad_pro |
iPad Pro 12.9" | 1024x1366 | 2 |
macbook_pro |
MacBook Pro | 1440x900 | 2 |
You can also pass any custom width and height instead of using a preset.
Common Use Cases
Responsive Design Testing
Capture the same page at multiple breakpoints to verify responsive layouts:
import { SnapRender } from 'snaprender';
const client = new SnapRender('YOUR_API_KEY');
const breakpoints = [
{ name: 'mobile', width: 375, height: 812 },
{ name: 'tablet', width: 768, height: 1024 },
{ name: 'desktop', width: 1440, height: 900 },
{ name: 'wide', width: 1920, height: 1080 }
];
for (const bp of breakpoints) {
const image = await client.screenshot({
url: 'https://example.com',
width: bp.width,
height: bp.height,
format: 'webp'
});
await writeFile(`${bp.name}.webp`, image);
console.log(`Captured: ${bp.name} (${bp.width}x${bp.height})`);
}
App Store / Marketing Screenshots
Generate device-framed screenshots for app store listings or marketing pages:
// Capture at device-native resolution
const image = await client.screenshot({
url: 'https://your-app.com/dashboard',
device: 'iphone_15_pro',
format: 'png'
});
// The image is at 3x DPR (1179x2556 actual pixels)
// Perfect for App Store screenshot requirements
Visual Regression Testing
Compare mobile screenshots over time to catch unintended layout changes:
import hashlib
from snaprender import SnapRender
client = SnapRender("YOUR_API_KEY")
def capture_and_compare(url: str, device: str, baseline_path: str) -> bool:
"""Capture a screenshot and compare to baseline."""
current = client.screenshot(url=url, device=device, format="png")
current_hash = hashlib.sha256(current).hexdigest()
try:
with open(baseline_path, "rb") as f:
baseline_hash = hashlib.sha256(f.read()).hexdigest()
if current_hash != baseline_hash:
print(f"Visual change detected for {device}!")
with open(f"diff_{device}.png", "wb") as f:
f.write(current)
return False
return True
except FileNotFoundError:
# No baseline exists, save current as baseline
with open(baseline_path, "wb") as f:
f.write(current)
return True
Dark Mode on Mobile
Combine device emulation with dark mode for complete coverage:
const image = await client.screenshot({
url: 'https://example.com',
device: 'iphone_15_pro',
dark_mode: true,
format: 'png'
});
Comparison
| Feature | Puppeteer/Playwright | Screenshot API |
|---|---|---|
| Setup time | 30-60 min | 2 min |
| Browser installation | Required (~150MB) | Not needed |
| Built-in device presets | Yes | Yes |
| Custom viewports | Yes | Yes |
| Dark mode | Yes | Yes |
| Ad blocking | Manual implementation | Built-in |
| Cookie banner removal | Manual implementation | Built-in |
| Memory usage | 50-300MB per capture | None (server-side) |
| Concurrent captures | Limited by RAM | Limited by plan |
Which Approach Should You Use?
Use Puppeteer or Playwright if:
- You need to interact with the page before capturing (login, click, scroll)
- You're already running browser automation for other tasks
- You want zero third-party dependencies
Use a screenshot API if:
- You just need the images without managing infrastructure
- You're capturing across many devices and want consistent results
- You need ad blocking and cookie banner removal without building it yourself
SnapRender supports all major device presets, dark mode, ad blocking, and cookie banner removal. 500 free screenshots per month, no credit card required. Get your API key and start capturing mobile screenshots in minutes.