How to Take Responsive Website Screenshots with Cypress Viewport Presets
Set Cypress viewport sizes, capture responsive layouts, and make screenshots repeatable with presets, explicit dimensions, and practical troubleshooting.
To take a responsive screenshot with Cypress, set the application viewport with cy.viewport(), wait for the layout or content you need, then call cy.screenshot() with capture: 'viewport'. Cypress defaults screenshot capture to fullPage, so specify viewport when you want only the visible screen.
This captures the page at a chosen viewport width and height. Cypress presets are convenient named dimensions; use explicit dimensions when you need to test a particular CSS breakpoint. Presets do not emulate a physical device or its device pixel ratio.
1. Capture the same page at several viewport sizes
This runnable spec visits your app at three example sizes and saves one viewport screenshot for each. The dimensions are examples, not claims about standard device sizes.
describe('responsive navigation', () => {
const sizes = [
{ name: 'phone', width: 375, height: 667 },
{ name: 'tablet', width: 768, height: 1024 },
{ name: 'desktop', width: 1280, height: 800 },
]
for (const size of sizes) {
it(`captures the ${size.name} layout`, () => {
cy.viewport(size.width, size.height)
cy.visit('/')
cy.get('nav').should('be.visible')
cy.screenshot(`responsive-${size.name}`, { capture: 'viewport' })
})
}
})
Save the spec in your Cypress end-to-end test directory and run it with your project’s configured Cypress test command. Cypress writes screenshots to the configured screenshots folder, which defaults to cypress/screenshots. Keep an assertion tied to the content that matters: it gives Cypress a condition to wait for and makes a failed or incomplete page easier to diagnose.
2. Choose a preset or exact dimensions
Use a documented preset
Pass a preset name to cy.viewport() when its dimensions suit the layout you want to inspect:
cy.viewport('iphone-6')
cy.visit('/')
cy.get('nav').should('be.visible')
cy.screenshot('navigation-iphone-6', { capture: 'viewport' })
Cypress documents presets including iphone-5 (320 × 568), iphone-6 (375 × 667), ipad-2 and ipad-mini (768 × 1024), and macbook-13 (1280 × 800). These are viewport dimensions, not full device simulations.
Set landscape orientation
The orientation argument reverses the preset’s width and height:
cy.viewport('iphone-6', 'landscape')
Target a CSS breakpoint directly
For responsive behavior, the useful dimensions are often the widths immediately around your actual CSS breakpoints. Use explicit dimensions so the test covers those exact widths:
const widths = [767, 768, 769]
for (const width of widths) {
it(`captures navigation at ${width}px`, () => {
cy.viewport(width, 900)
cy.visit('/')
cy.get('nav').should('be.visible')
cy.screenshot(`navigation-${width}px`, { capture: 'viewport' })
})
}
Adjust those widths and the height to match your application’s breakpoints and content. Checking both sides of a breakpoint can reveal wrapping, overflow, and navigation changes that a single preset misses.
3. Set defaults and test-specific viewports
Cypress starts with an application viewport of 1000 × 660 pixels unless project configuration changes it. Set project-wide defaults with viewportWidth and viewportHeight in the Cypress configuration:
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
viewportWidth: 1280,
viewportHeight: 800,
},
})
You can also scope dimensions to a suite or test using test configuration. For an in-test change, use cy.viewport(). Cypress resets the viewport to its configured default between tests, so set the intended viewport in each test that depends on it.
Starting with Cypress 16.0.0, changing viewportWidth or viewportHeight with Cypress.config() during test execution throws. Use cy.viewport() for a runtime change, or configure the dimensions for the suite or test.
4. Choose the screenshot capture mode
| Goal | How | What it captures |
|---|---|---|
| One responsive screen | cy.screenshot(name, { capture: 'viewport' }) |
The application in the current viewport |
| The page from top to bottom | cy.screenshot(name, { capture: 'fullPage' }) |
The full application page; this is Cypress’s default capture mode |
| The app with Cypress context | cy.screenshot(name, { capture: 'runner' }) |
The browser viewport and Cypress Command Log |
| A particular element | cy.get(selector).screenshot(name) |
The selected element |
Use viewport capture for responsive screen comparisons. Full-page capture answers a different question: how the entire page looks vertically. Element capture is useful when the component itself is the subject of the check.
5. Make screenshots repeatable
- Hold the viewport constant. Use the same width and height for each screenshot you intend to compare.
- Wait for the relevant UI. Assert on a visible element or other meaningful ready condition before capturing.
- Control motion and changing content. Screenshot options can disable JavaScript timers and CSS animations, black out selected elements, and run synchronous callbacks before or after capture. Use these when clocks, animations, or sensitive content make images unstable.
- Keep the rendering environment consistent. Browser version, operating system, display scaling, and installed fonts can create small pixel differences. Generate comparison images in the same environment where possible.
- Do not rely on runner preview scale. Cypress may scale its preview to fit the runner; that display scaling does not affect application calculations or behavior.
Action-command animation settings alone do not guarantee that unrelated page animations are absent from a snapshot. Configure screenshot capture itself when animation is the source of visual variation.
6. Know what Cypress screenshots do and do not cover
cy.screenshot() saves an image; it does not compare the image against a baseline. If the requirement is visual regression detection or cross-browser rendering comparison, use a visual-testing workflow designed for comparison. Cypress’s visual-testing guide describes Percy by BrowserStack as a service that captures DOM snapshots during Cypress tests and renders them across browsers and responsive widths. Check the service’s current documentation for its present capabilities and terms.
A Cypress preset gives you a viewport size shortcut. Cypress does not simulate device pixel ratio, so a preset alone does not reproduce every rendering characteristic of a physical handset. For responsive layout tests, focus on the viewport width and height your CSS responds to.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The screenshot is taller than the visible screen | cy.screenshot() uses the default fullPage capture mode. |
Pass { capture: 'viewport' }. |
| The layout has the wrong width in one test | The test relies on a default or a viewport set by another test. | Call cy.viewport() in the test that captures the image; Cypress resets the viewport to its configured default between tests. |
Cypress.config() throws while changing viewport settings |
In Cypress 16.0.0 and later, changing viewportWidth or viewportHeight during test execution is disallowed. |
Use cy.viewport(width, height) at runtime or configure the dimensions for the test or suite. |
| A preset does not look like a physical phone | The preset sets viewport dimensions but does not simulate device pixel ratio. | Use it to test CSS layout at those dimensions; do not treat it as complete handset emulation. |
| The image contains a loading state or missing content | The capture happened before the relevant UI was ready. | Wait for a meaningful selector or application-ready condition before taking the screenshot. |
| Pixel comparisons differ slightly between runs or machines | Fonts, browser version, operating system, or display scaling differ; animation or changing content may also be present. | Fix the rendering environment, stabilize the page, and use screenshot options to control animations, timers, or changing regions. |
| A screenshot exists, but no visual test fails when it changes | Cypress’s built-in screenshot command only captures an image; it does not compare images. | Add a visual comparison tool or workflow if baseline comparison is required. |
8. Performance, reliability, and cost
Every additional viewport means another test capture and another rendering pass through the test flow. Choose widths that cover your breakpoints and important layouts rather than taking redundant images at many nearly identical sizes. Waiting for the application’s relevant ready condition improves reliability; arbitrary short waits can leave slow content uncaptured, while unnecessary long waits add time to every run.
Keep screenshot runs reproducible by pinning the browser and using a consistent operating environment for comparisons. Cypress saves files locally in the configured screenshots directory, and can save failure screenshots during cypress run. Cypress screenshots are generated by your test run; any separate visual-testing service has its own pricing and terms.
Or skip the browser setup
If you need a screenshot from a URL outside a Cypress test, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF, and its API documentation covers the available options.
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}`)
await Bun.write('shot.webp', res)
- Cookie banners are accepted like a visitor, and cookie/consent banners, newsletter popups, and chat widgets from more than 60 known platforms are removed before the shot. Each cleanup step can be turned off.
- Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are never billed. Responses identify the page verdict and billing status in headers.
- An MCP server lets Claude, Cursor, and other MCP clients use
take_screenshot,get_page_info, andcapture_pdf. - The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan.
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.
FAQ
Does changing the viewport resize the browser window?
cy.viewport() changes the application viewport used by the test. Cypress may scale its runner preview to fit; that preview scaling does not change how the application calculates its layout.
Should I use a preset for every responsive test?
No. Use a preset when its named dimensions suit your test. Use explicit width and height when the exact breakpoint or design size matters.
Can Cypress capture screenshots automatically when a test fails?
Cypress can save failure screenshots during cypress run. Screenshot output is saved under the configured screenshots folder, which defaults to cypress/screenshots.
Can I use these screenshots as proof that a page works on a real phone?
They show your application at a chosen viewport. A preset does not simulate device pixel ratio or every physical-device characteristic, so treat it as a responsive layout check rather than complete device emulation.


