How to Take Mobile Website Screenshots with Cypress Viewport Settings
Set a mobile-sized Cypress viewport, wait for the right page state, and capture the visible screen or full page. Includes configuration, troubleshooting, and a ScreenshotNeo option.
Set the browser viewport with cy.viewport(width, height) or a Cypress device preset, visit the page, wait until the responsive state you want is visible, and call cy.screenshot(). Use { capture: 'viewport' } to save the visible application area, or { capture: 'fullPage' } to capture the page from top to bottom.
describe('mobile layout', () => {
it('captures the mobile home page', () => {
cy.viewport(375, 667)
cy.visit('/')
cy.get('h1').should('be.visible')
cy.screenshot('mobile-home', { capture: 'viewport' })
})
})
The example assumes the app is available at the base URL configured for Cypress and that a visible heading indicates the page is ready. Replace that assertion with one that represents the state your screenshot needs. Cypress viewport settings test layout dimensions; they do not emulate every physical-device characteristic.
1. Set a mobile viewport
Call cy.viewport() before visiting the page or before the actions that depend on the viewport. It accepts explicit width and height in pixels, or a named preset from Cypress’s documented list. See the Cypress viewport API.
// Explicit dimensions: useful for testing a breakpoint
cy.viewport(375, 667)
// Named preset
cy.viewport('iphone-6')
// Preset in landscape
cy.viewport('iphone-6', 'landscape')
| Preset | Portrait dimensions | Landscape dimensions |
|---|---|---|
iphone-5 |
320 × 568 | 568 × 320 |
iphone-6 |
375 × 667 | 667 × 375 |
iphone-x |
375 × 812 | 812 × 375 |
samsung-s10 |
360 × 760 | 760 × 360 |
Use an explicit size when you need to exercise a CSS breakpoint or match a project’s target viewport precisely. Preset labels provide convenient dimensions; they do not make the test equivalent to running on that physical phone. Cypress documents that devicePixelRatio is not simulated.
2. Capture the viewport or the full page
cy.screenshot() supports several capture modes. For a mobile website screenshot, the distinction between the visible viewport and a full-page capture matters: a full-page image may be much taller than a phone screen.
| Capture mode | What it includes | When to use it |
|---|---|---|
viewport |
The application content in the current viewport | Check the initial mobile screen, header, or above-the-fold layout |
fullPage |
The application page from top to bottom | Review a whole page or save a long-page reference |
runner |
The browser viewport including Cypress Command Log chrome, subject to Cypress’s documented behavior | Capture test-runner context for debugging |
Manual screenshots default to fullPage. Specify the mode when the output needs a predictable scope. The Cypress screenshot API documents capture modes, clipping, element screenshots, and callbacks.
// Visible application area
cy.screenshot('mobile-viewport', { capture: 'viewport' })
// Entire page, top to bottom
cy.screenshot('mobile-full-page', { capture: 'fullPage' })
// Crop a rectangle from the viewport
cy.screenshot('mobile-crop', {
capture: 'viewport',
clip: { x: 0, y: 0, width: 375, height: 300 },
})
// Capture one element
cy.get('[data-testid="product-card"]').screenshot('product-card')
A clip is expressed in pixels with x, y, width, and height. Make sure the requested rectangle fits the image you intend to capture. For an element capture, select the element after the page has rendered and use the element’s .screenshot() command.
3. Wait for the state you want to save
Screenshot capture is asynchronous. The application can change after the command is issued and before the image is finished, so establish the state with Cypress assertions and commands first. Avoid arbitrary sleeps when a visible element, loaded data, or completed transition can be asserted directly.
describe('mobile product page', () => {
it('captures a product after its data loads', () => {
cy.viewport(375, 812)
cy.visit('/products/example')
cy.get('[data-testid="product-title"]')
.should('be.visible')
.and('contain.text', 'Example')
cy.get('[data-testid="product-price"]').should('be.visible')
cy.screenshot('product-mobile', { capture: 'viewport' })
})
})
For a page whose content appears after a request, wait on the relevant request or assert on the resulting content. For pages with animations, capture after the UI reaches its settled state. Cypress screenshot defaults normally disable JavaScript timers and CSS animations during capture; those behaviors can be customized through screenshot configuration.
4. Configure project defaults and save location
Cypress’s default viewport is 1000 × 660 pixels. Set project defaults in Cypress configuration when most tests use the same dimensions, or set the viewport in a test for a specific mobile scenario. The default screenshot folder is cypress/screenshots.
// cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
viewportWidth: 375,
viewportHeight: 667,
screenshotsFolder: 'cypress/screenshots',
e2e: {
baseUrl: 'http://localhost:3000',
},
})
A per-test viewport can override the configured defaults:
it('uses a narrow viewport for this case', () => {
cy.viewport(320, 568)
cy.visit('/')
cy.get('main').should('be.visible')
cy.screenshot('narrow-mobile', { capture: 'viewport' })
})
Cypress resets the viewport to its configured default between tests. Starting with Cypress 16.0.0, changing viewportWidth or viewportHeight through Cypress.config() during test execution throws. Use cy.viewport() or test configuration for runtime test sizing. See the configuration reference and Cypress.config() API.
5. Run the test and find the screenshot
Manual cy.screenshot() calls work in Cypress open mode and run mode. With the default configuration, a failed test in cypress run also gets an automatic failure screenshot; Cypress does not automatically take failure screenshots in open mode. Configure screenshotOnRunFailure if you need to change failure-screenshot behavior.
# Run the end-to-end suite in the terminal
npx cypress run
Look in the configured screenshot folder, which defaults to cypress/screenshots. Manual screenshot names become image files there. For the failure behavior and related settings, see Cypress screenshots and videos.
6. Make mobile screenshots repeatable
A screenshot is an image capture; cy.screenshot() does not compare it with a baseline. If you are checking visual changes, keep the viewport and execution environment consistent across baseline and candidate images. Cypress notes that operating systems, browser versions, display scaling, and installed fonts can change rendered output. Its visual testing guide describes the comparison workflow and available integrations.
- Use the same explicit viewport dimensions or preset for every run.
- Keep browser, operating system, and installed fonts consistent when comparing images.
- Assert that the responsive state and important page content are present before capture.
- Wait for meaningful application readiness instead of capturing during a loading transition.
- Choose
viewportorfullPagedeliberately so image dimensions remain consistent.
7. Troubleshooting
The screenshot has desktop dimensions
Cause: The test did not set the viewport before capture, or the viewport was reset between tests. Fix: Call cy.viewport() in the test that captures the image, or configure viewportWidth and viewportHeight as project defaults.
The screenshot is much taller than expected
Cause: Manual screenshots use fullPage by default. Fix: Pass { capture: 'viewport' } for only the visible application area.
The screenshot shows a loading state or incomplete content
Cause: The screenshot was requested before the relevant UI appeared, or capture overlapped a changing page state. Fix: Assert on the page content, data, or state that matters before calling cy.screenshot().
The image differs across machines
Cause: Browser versions, operating systems, display scaling, or fonts can affect rendering. Fix: Use the same execution environment for both images and hold the viewport constant. Cypress viewport sizing does not simulate device pixel ratio or fully reproduce a physical phone.
Changing viewport configuration at runtime throws
Cause: In Cypress 16.0.0 and later, changing viewportWidth or viewportHeight via Cypress.config() during a test is unsupported. Fix: Call cy.viewport(width, height), or provide dimensions through Cypress or test configuration.
No failure screenshot appears in open mode
Cause: Automatic failure screenshots are enabled by default for cypress run, not cypress open. Fix: Add an explicit cy.screenshot() call when you need a manual capture in open mode, or run the suite in run mode for automatic failure captures.
8. Performance, reliability, and cost
For a small set of responsive checks, Cypress captures images as part of the browser test flow. Keep each screenshot purposeful: full-page captures produce taller images, while viewport captures focus on the mobile screen under test. Waiting for an application-specific condition improves reliability and avoids saving transient loading states.
Cypress’s screenshot command is not a visual diff engine. If you need regression detection, use a comparison workflow and keep its rendering environment fixed. The Cypress visual testing guide lists integrations; verify current product capabilities and terms directly before selecting one. No service is required just to set a viewport and save an image. Project costs depend on the Cypress setup and any comparison service you choose; the cited Cypress API documentation does not establish a general price.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A GET request returns a PNG, JPEG, WebP, or PDF capture. Its API documentation covers the parameters, including the names used by other screenshot APIs.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' })
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`)
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`)
const bytes = new Uint8Array(await res.arrayBuffer())
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes))
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, and failed loads are never billed. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Plans include every feature, and yearly billing gives two months free. Sign up free and get 1,000 screenshots a month with no card.
FAQ
Does a Cypress mobile viewport emulate a real phone?
No. It sets the browser viewport dimensions and orientation. Cypress documents that devicePixelRatio is not simulated, so it is useful for responsive layout checks but is not a full device emulation.
How do I take a screenshot at a custom breakpoint?
Pass the breakpoint’s desired dimensions directly, such as cy.viewport(390, 844), then assert that the expected layout is visible before capturing.
Can Cypress capture just one element?
Yes. Select the element and call .screenshot() on it. This is useful when the component matters more than the page around it.
Does Cypress compare screenshots automatically?
No. cy.screenshot() saves an image. A separate visual comparison workflow is needed to detect differences between a baseline and a later capture.


